AtmosphereSystem.Processing.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. using Content.Server.Atmos.Components;
  2. using Content.Server.Atmos.Piping.Components;
  3. using Content.Shared.Atmos;
  4. using Content.Shared.Atmos.Components;
  5. using Content.Shared.Maps;
  6. using Robust.Shared.Map;
  7. using Robust.Shared.Map.Components;
  8. using Robust.Shared.Physics.Components;
  9. using Robust.Shared.Timing;
  10. using Robust.Shared.Utility;
  11. namespace Content.Server.Atmos.EntitySystems
  12. {
  13. public sealed partial class AtmosphereSystem
  14. {
  15. [Dependency] private readonly IGameTiming _gameTiming = default!;
  16. private readonly Stopwatch _simulationStopwatch = new();
  17. /// <summary>
  18. /// Check current execution time every n instances processed.
  19. /// </summary>
  20. private const int LagCheckIterations = 30;
  21. /// <summary>
  22. /// Check current execution time every n instances processed.
  23. /// </summary>
  24. private const int InvalidCoordinatesLagCheckIterations = 50;
  25. private int _currentRunAtmosphereIndex;
  26. private bool _simulationPaused;
  27. private TileAtmosphere GetOrNewTile(EntityUid owner, GridAtmosphereComponent atmosphere, Vector2i index, bool invalidateNew = true)
  28. {
  29. var tile = atmosphere.Tiles.GetOrNew(index, out var existing);
  30. if (existing)
  31. return tile;
  32. if (invalidateNew)
  33. atmosphere.InvalidatedCoords.Add(index);
  34. tile.GridIndex = owner;
  35. tile.GridIndices = index;
  36. return tile;
  37. }
  38. private readonly List<Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>> _currentRunAtmosphere = new();
  39. /// <summary>
  40. /// Revalidates all invalid coordinates in a grid atmosphere.
  41. /// I.e., process any tiles that have had their airtight blockers modified.
  42. /// </summary>
  43. /// <param name="ent">The grid atmosphere in question.</param>
  44. /// <returns>Whether the process succeeded or got paused due to time constrains.</returns>
  45. private bool ProcessRevalidate(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  46. {
  47. if (ent.Comp4.MapUid == null)
  48. {
  49. Log.Error($"Attempted to process atmosphere on a map-less grid? Grid: {ToPrettyString(ent)}");
  50. return true;
  51. }
  52. var (uid, atmosphere, visuals, grid, xform) = ent;
  53. var volume = GetVolumeForTiles(grid);
  54. TryComp(xform.MapUid, out MapAtmosphereComponent? mapAtmos);
  55. if (!atmosphere.ProcessingPaused)
  56. {
  57. atmosphere.CurrentRunInvalidatedTiles.Clear();
  58. atmosphere.CurrentRunInvalidatedTiles.EnsureCapacity(atmosphere.InvalidatedCoords.Count);
  59. foreach (var indices in atmosphere.InvalidatedCoords)
  60. {
  61. var tile = GetOrNewTile(uid, atmosphere, indices, invalidateNew: false);
  62. atmosphere.CurrentRunInvalidatedTiles.Enqueue(tile);
  63. // Update tile.IsSpace and tile.MapAtmosphere, and tile.AirtightData.
  64. UpdateTileData(ent, mapAtmos, tile);
  65. }
  66. atmosphere.InvalidatedCoords.Clear();
  67. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  68. return false;
  69. }
  70. var number = 0;
  71. while (atmosphere.CurrentRunInvalidatedTiles.TryDequeue(out var tile))
  72. {
  73. DebugTools.Assert(atmosphere.Tiles.GetValueOrDefault(tile.GridIndices) == tile);
  74. UpdateAdjacentTiles(ent, tile, activate: true);
  75. UpdateTileAir(ent, tile, volume);
  76. InvalidateVisuals(ent, tile);
  77. if (number++ < InvalidCoordinatesLagCheckIterations)
  78. continue;
  79. number = 0;
  80. // Process the rest next time.
  81. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  82. return false;
  83. }
  84. TrimDisconnectedMapTiles(ent);
  85. return true;
  86. }
  87. /// <summary>
  88. /// This method queued a tile and all of its neighbours up for processing by <see cref="TrimDisconnectedMapTiles"/>.
  89. /// </summary>
  90. public void QueueTileTrim(GridAtmosphereComponent atmos, TileAtmosphere tile)
  91. {
  92. if (!tile.TrimQueued)
  93. {
  94. tile.TrimQueued = true;
  95. atmos.PossiblyDisconnectedTiles.Add(tile);
  96. }
  97. for (var i = 0; i < Atmospherics.Directions; i++)
  98. {
  99. var direction = (AtmosDirection) (1 << i);
  100. var indices = tile.GridIndices.Offset(direction);
  101. if (atmos.Tiles.TryGetValue(indices, out var adj)
  102. && adj.NoGridTile
  103. && !adj.TrimQueued)
  104. {
  105. adj.TrimQueued = true;
  106. atmos.PossiblyDisconnectedTiles.Add(adj);
  107. }
  108. }
  109. }
  110. /// <summary>
  111. /// Tiles in a <see cref="GridAtmosphereComponent"/> are either grid-tiles, or they they should be are tiles
  112. /// adjacent to grid-tiles that represent the map's atmosphere. This method trims any map-tiles that are no longer
  113. /// adjacent to any grid-tiles.
  114. /// </summary>
  115. private void TrimDisconnectedMapTiles(
  116. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  117. {
  118. var atmos = ent.Comp1;
  119. foreach (var tile in atmos.PossiblyDisconnectedTiles)
  120. {
  121. tile.TrimQueued = false;
  122. if (!tile.NoGridTile)
  123. continue;
  124. var connected = false;
  125. for (var i = 0; i < Atmospherics.Directions; i++)
  126. {
  127. var indices = tile.GridIndices.Offset((AtmosDirection) (1 << i));
  128. if (_map.TryGetTile(ent.Comp3, indices, out var gridTile) && !gridTile.IsEmpty)
  129. {
  130. connected = true;
  131. break;
  132. }
  133. }
  134. if (!connected)
  135. {
  136. RemoveActiveTile(atmos, tile);
  137. atmos.Tiles.Remove(tile.GridIndices);
  138. }
  139. }
  140. atmos.PossiblyDisconnectedTiles.Clear();
  141. }
  142. /// <summary>
  143. /// Checks whether a tile has a corresponding grid-tile, or whether it is a "map" tile. Also checks whether the
  144. /// tile should be considered "space"
  145. /// </summary>
  146. private void UpdateTileData(
  147. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
  148. MapAtmosphereComponent? mapAtmos,
  149. TileAtmosphere tile)
  150. {
  151. var idx = tile.GridIndices;
  152. bool mapAtmosphere;
  153. if (_map.TryGetTile(ent.Comp3, idx, out var gTile) && !gTile.IsEmpty)
  154. {
  155. var contentDef = (ContentTileDefinition) _tileDefinitionManager[gTile.TypeId];
  156. mapAtmosphere = contentDef.MapAtmosphere;
  157. tile.ThermalConductivity = contentDef.ThermalConductivity;
  158. tile.HeatCapacity = contentDef.HeatCapacity;
  159. tile.NoGridTile = false;
  160. }
  161. else
  162. {
  163. mapAtmosphere = true;
  164. tile.ThermalConductivity = 0.5f;
  165. tile.HeatCapacity = float.PositiveInfinity;
  166. if (!tile.NoGridTile)
  167. {
  168. tile.NoGridTile = true;
  169. // This tile just became a non-grid atmos tile.
  170. // It, or one of its neighbours, might now be completely disconnected from the grid.
  171. QueueTileTrim(ent.Comp1, tile);
  172. }
  173. }
  174. UpdateAirtightData(ent.Owner, ent.Comp1, ent.Comp3, tile);
  175. if (mapAtmosphere)
  176. {
  177. if (!tile.MapAtmosphere)
  178. {
  179. (tile.Air, tile.Space) = GetDefaultMapAtmosphere(mapAtmos);
  180. tile.MapAtmosphere = true;
  181. ent.Comp1.MapTiles.Add(tile);
  182. }
  183. DebugTools.AssertNotNull(tile.Air);
  184. DebugTools.Assert(tile.Air?.Immutable ?? false);
  185. return;
  186. }
  187. if (!tile.MapAtmosphere)
  188. return;
  189. // Tile used to be exposed to the map's atmosphere, but isn't anymore.
  190. RemoveMapAtmos(ent.Comp1, tile);
  191. }
  192. private void RemoveMapAtmos(GridAtmosphereComponent atmos, TileAtmosphere tile)
  193. {
  194. DebugTools.Assert(tile.MapAtmosphere);
  195. DebugTools.AssertNotNull(tile.Air);
  196. DebugTools.Assert(tile.Air?.Immutable ?? false);
  197. tile.MapAtmosphere = false;
  198. atmos.MapTiles.Remove(tile);
  199. tile.Air = null;
  200. tile.AirArchived = null;
  201. tile.ArchivedCycle = 0;
  202. tile.LastShare = 0f;
  203. tile.Space = false;
  204. }
  205. /// <summary>
  206. /// Check whether a grid-tile should have an air mixture, and give it one if it doesn't already have one.
  207. /// </summary>
  208. private void UpdateTileAir(
  209. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
  210. TileAtmosphere tile,
  211. float volume)
  212. {
  213. if (tile.MapAtmosphere)
  214. {
  215. DebugTools.AssertNotNull(tile.Air);
  216. DebugTools.Assert(tile.Air?.Immutable ?? false);
  217. return;
  218. }
  219. var data = tile.AirtightData;
  220. var fullyBlocked = data.BlockedDirections == AtmosDirection.All;
  221. if (fullyBlocked && data.NoAirWhenBlocked)
  222. {
  223. if (tile.Air == null)
  224. return;
  225. tile.Air = null;
  226. tile.AirArchived = null;
  227. tile.ArchivedCycle = 0;
  228. tile.LastShare = 0f;
  229. tile.Hotspot = new Hotspot();
  230. return;
  231. }
  232. if (tile.Air != null)
  233. return;
  234. tile.Air = new GasMixture(volume){Temperature = Atmospherics.T20C};
  235. if (data.FixVacuum)
  236. GridFixTileVacuum(tile);
  237. }
  238. private void QueueRunTiles(
  239. Queue<TileAtmosphere> queue,
  240. HashSet<TileAtmosphere> tiles)
  241. {
  242. queue.Clear();
  243. queue.EnsureCapacity(tiles.Count);
  244. foreach (var tile in tiles)
  245. {
  246. queue.Enqueue(tile);
  247. }
  248. }
  249. private bool ProcessTileEqualize(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  250. {
  251. var atmosphere = ent.Comp1;
  252. if (!atmosphere.ProcessingPaused)
  253. QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
  254. var number = 0;
  255. while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
  256. {
  257. EqualizePressureInZone(ent, tile, atmosphere.UpdateCounter);
  258. if (number++ < LagCheckIterations)
  259. continue;
  260. number = 0;
  261. // Process the rest next time.
  262. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  263. {
  264. return false;
  265. }
  266. }
  267. return true;
  268. }
  269. private bool ProcessActiveTiles(
  270. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  271. {
  272. var atmosphere = ent.Comp1;
  273. if(!atmosphere.ProcessingPaused)
  274. QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
  275. var number = 0;
  276. while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
  277. {
  278. ProcessCell(ent, tile, atmosphere.UpdateCounter);
  279. if (number++ < LagCheckIterations)
  280. continue;
  281. number = 0;
  282. // Process the rest next time.
  283. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  284. {
  285. return false;
  286. }
  287. }
  288. return true;
  289. }
  290. private bool ProcessExcitedGroups(
  291. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  292. {
  293. var gridAtmosphere = ent.Comp1;
  294. if (!gridAtmosphere.ProcessingPaused)
  295. {
  296. gridAtmosphere.CurrentRunExcitedGroups.Clear();
  297. gridAtmosphere.CurrentRunExcitedGroups.EnsureCapacity(gridAtmosphere.ExcitedGroups.Count);
  298. foreach (var group in gridAtmosphere.ExcitedGroups)
  299. {
  300. gridAtmosphere.CurrentRunExcitedGroups.Enqueue(group);
  301. }
  302. }
  303. var number = 0;
  304. while (gridAtmosphere.CurrentRunExcitedGroups.TryDequeue(out var excitedGroup))
  305. {
  306. excitedGroup.BreakdownCooldown++;
  307. excitedGroup.DismantleCooldown++;
  308. if (excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles)
  309. ExcitedGroupSelfBreakdown(ent, excitedGroup);
  310. else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
  311. DeactivateGroupTiles(gridAtmosphere, excitedGroup);
  312. // TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it?
  313. if (number++ < LagCheckIterations)
  314. continue;
  315. number = 0;
  316. // Process the rest next time.
  317. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  318. {
  319. return false;
  320. }
  321. }
  322. return true;
  323. }
  324. private bool ProcessHighPressureDelta(Entity<GridAtmosphereComponent> ent)
  325. {
  326. var atmosphere = ent.Comp;
  327. if (!atmosphere.ProcessingPaused)
  328. QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HighPressureDelta);
  329. // Note: This is still processed even if space wind is turned off since this handles playing the sounds.
  330. var number = 0;
  331. var bodies = EntityManager.GetEntityQuery<PhysicsComponent>();
  332. var xforms = EntityManager.GetEntityQuery<TransformComponent>();
  333. var metas = EntityManager.GetEntityQuery<MetaDataComponent>();
  334. var pressureQuery = EntityManager.GetEntityQuery<MovedByPressureComponent>();
  335. while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
  336. {
  337. HighPressureMovements(ent, tile, bodies, xforms, pressureQuery, metas);
  338. tile.PressureDifference = 0f;
  339. tile.LastPressureDirection = tile.PressureDirection;
  340. tile.PressureDirection = AtmosDirection.Invalid;
  341. tile.PressureSpecificTarget = null;
  342. atmosphere.HighPressureDelta.Remove(tile);
  343. if (number++ < LagCheckIterations)
  344. continue;
  345. number = 0;
  346. // Process the rest next time.
  347. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  348. {
  349. return false;
  350. }
  351. }
  352. return true;
  353. }
  354. private bool ProcessHotspots(
  355. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
  356. {
  357. var atmosphere = ent.Comp1;
  358. if(!atmosphere.ProcessingPaused)
  359. QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HotspotTiles);
  360. var number = 0;
  361. while (atmosphere.CurrentRunTiles.TryDequeue(out var hotspot))
  362. {
  363. ProcessHotspot(ent, hotspot);
  364. if (number++ < LagCheckIterations)
  365. continue;
  366. number = 0;
  367. // Process the rest next time.
  368. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  369. {
  370. return false;
  371. }
  372. }
  373. return true;
  374. }
  375. private bool ProcessSuperconductivity(GridAtmosphereComponent atmosphere)
  376. {
  377. if(!atmosphere.ProcessingPaused)
  378. QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.SuperconductivityTiles);
  379. var number = 0;
  380. while (atmosphere.CurrentRunTiles.TryDequeue(out var superconductivity))
  381. {
  382. Superconduct(atmosphere, superconductivity);
  383. if (number++ < LagCheckIterations)
  384. continue;
  385. number = 0;
  386. // Process the rest next time.
  387. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  388. {
  389. return false;
  390. }
  391. }
  392. return true;
  393. }
  394. private bool ProcessPipeNets(GridAtmosphereComponent atmosphere)
  395. {
  396. if (!atmosphere.ProcessingPaused)
  397. {
  398. atmosphere.CurrentRunPipeNet.Clear();
  399. atmosphere.CurrentRunPipeNet.EnsureCapacity(atmosphere.PipeNets.Count);
  400. foreach (var net in atmosphere.PipeNets)
  401. {
  402. atmosphere.CurrentRunPipeNet.Enqueue(net);
  403. }
  404. }
  405. var number = 0;
  406. while (atmosphere.CurrentRunPipeNet.TryDequeue(out var pipenet))
  407. {
  408. pipenet.Update();
  409. if (number++ < LagCheckIterations)
  410. continue;
  411. number = 0;
  412. // Process the rest next time.
  413. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  414. {
  415. return false;
  416. }
  417. }
  418. return true;
  419. }
  420. /**
  421. * UpdateProcessing() takes a different number of calls to go through all of atmos
  422. * processing depending on what options are enabled. This returns the actual effective time
  423. * between atmos updates that devices actually experience.
  424. */
  425. public float RealAtmosTime()
  426. {
  427. int num = (int)AtmosphereProcessingState.NumStates;
  428. if (!MonstermosEqualization)
  429. num--;
  430. if (!ExcitedGroups)
  431. num--;
  432. if (!Superconduction)
  433. num--;
  434. return num * AtmosTime;
  435. }
  436. private bool ProcessAtmosDevices(
  437. Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
  438. Entity<MapAtmosphereComponent?> map)
  439. {
  440. var atmosphere = ent.Comp1;
  441. if (!atmosphere.ProcessingPaused)
  442. {
  443. atmosphere.CurrentRunAtmosDevices.Clear();
  444. atmosphere.CurrentRunAtmosDevices.EnsureCapacity(atmosphere.AtmosDevices.Count);
  445. foreach (var device in atmosphere.AtmosDevices)
  446. {
  447. atmosphere.CurrentRunAtmosDevices.Enqueue(device);
  448. }
  449. }
  450. var time = _gameTiming.CurTime;
  451. var number = 0;
  452. var ev = new AtmosDeviceUpdateEvent(RealAtmosTime(), (ent, ent.Comp1, ent.Comp2), map);
  453. while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
  454. {
  455. RaiseLocalEvent(device, ref ev);
  456. device.Comp.LastProcess = time;
  457. if (number++ < LagCheckIterations)
  458. continue;
  459. number = 0;
  460. // Process the rest next time.
  461. if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
  462. {
  463. return false;
  464. }
  465. }
  466. return true;
  467. }
  468. private void UpdateProcessing(float frameTime)
  469. {
  470. _simulationStopwatch.Restart();
  471. if (!_simulationPaused)
  472. {
  473. _currentRunAtmosphereIndex = 0;
  474. _currentRunAtmosphere.Clear();
  475. var query = EntityQueryEnumerator<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>();
  476. while (query.MoveNext(out var uid, out var atmos, out var overlay, out var grid, out var xform ))
  477. {
  478. _currentRunAtmosphere.Add((uid, atmos, overlay, grid, xform));
  479. }
  480. }
  481. // We set this to true just in case we have to stop processing due to time constraints.
  482. _simulationPaused = true;
  483. for (; _currentRunAtmosphereIndex < _currentRunAtmosphere.Count; _currentRunAtmosphereIndex++)
  484. {
  485. var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex];
  486. var (owner, atmosphere, visuals, grid, xform) = ent;
  487. if (xform.MapUid == null
  488. || TerminatingOrDeleted(xform.MapUid.Value)
  489. || xform.MapID == MapId.Nullspace)
  490. {
  491. Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(xform?.MapUid)}. MapId: {xform?.MapID}");
  492. continue;
  493. }
  494. if (atmosphere.LifeStage >= ComponentLifeStage.Stopping || Paused(owner) || !atmosphere.Simulated)
  495. continue;
  496. atmosphere.Timer += frameTime;
  497. if (atmosphere.Timer < AtmosTime)
  498. continue;
  499. // We subtract it so it takes lost time into account.
  500. atmosphere.Timer -= AtmosTime;
  501. var map = new Entity<MapAtmosphereComponent?>(xform.MapUid.Value, _mapAtmosQuery.CompOrNull(xform.MapUid.Value));
  502. switch (atmosphere.State)
  503. {
  504. case AtmosphereProcessingState.Revalidate:
  505. if (!ProcessRevalidate(ent))
  506. {
  507. atmosphere.ProcessingPaused = true;
  508. return;
  509. }
  510. atmosphere.ProcessingPaused = false;
  511. // Next state depends on whether monstermos equalization is enabled or not.
  512. // Note: We do this here instead of on the tile equalization step to prevent ending it early.
  513. // Therefore, a change to this CVar might only be applied after that step is over.
  514. atmosphere.State = MonstermosEqualization
  515. ? AtmosphereProcessingState.TileEqualize
  516. : AtmosphereProcessingState.ActiveTiles;
  517. continue;
  518. case AtmosphereProcessingState.TileEqualize:
  519. if (!ProcessTileEqualize(ent))
  520. {
  521. atmosphere.ProcessingPaused = true;
  522. return;
  523. }
  524. atmosphere.ProcessingPaused = false;
  525. atmosphere.State = AtmosphereProcessingState.ActiveTiles;
  526. continue;
  527. case AtmosphereProcessingState.ActiveTiles:
  528. if (!ProcessActiveTiles(ent))
  529. {
  530. atmosphere.ProcessingPaused = true;
  531. return;
  532. }
  533. atmosphere.ProcessingPaused = false;
  534. // Next state depends on whether excited groups are enabled or not.
  535. atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta;
  536. continue;
  537. case AtmosphereProcessingState.ExcitedGroups:
  538. if (!ProcessExcitedGroups(ent))
  539. {
  540. atmosphere.ProcessingPaused = true;
  541. return;
  542. }
  543. atmosphere.ProcessingPaused = false;
  544. atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
  545. continue;
  546. case AtmosphereProcessingState.HighPressureDelta:
  547. if (!ProcessHighPressureDelta((ent, ent)))
  548. {
  549. atmosphere.ProcessingPaused = true;
  550. return;
  551. }
  552. atmosphere.ProcessingPaused = false;
  553. atmosphere.State = AtmosphereProcessingState.Hotspots;
  554. continue;
  555. case AtmosphereProcessingState.Hotspots:
  556. if (!ProcessHotspots(ent))
  557. {
  558. atmosphere.ProcessingPaused = true;
  559. return;
  560. }
  561. atmosphere.ProcessingPaused = false;
  562. // Next state depends on whether superconduction is enabled or not.
  563. // Note: We do this here instead of on the tile equalization step to prevent ending it early.
  564. // Therefore, a change to this CVar might only be applied after that step is over.
  565. atmosphere.State = Superconduction
  566. ? AtmosphereProcessingState.Superconductivity
  567. : AtmosphereProcessingState.PipeNet;
  568. continue;
  569. case AtmosphereProcessingState.Superconductivity:
  570. if (!ProcessSuperconductivity(atmosphere))
  571. {
  572. atmosphere.ProcessingPaused = true;
  573. return;
  574. }
  575. atmosphere.ProcessingPaused = false;
  576. atmosphere.State = AtmosphereProcessingState.PipeNet;
  577. continue;
  578. case AtmosphereProcessingState.PipeNet:
  579. if (!ProcessPipeNets(atmosphere))
  580. {
  581. atmosphere.ProcessingPaused = true;
  582. return;
  583. }
  584. atmosphere.ProcessingPaused = false;
  585. atmosphere.State = AtmosphereProcessingState.AtmosDevices;
  586. continue;
  587. case AtmosphereProcessingState.AtmosDevices:
  588. if (!ProcessAtmosDevices(ent, map))
  589. {
  590. atmosphere.ProcessingPaused = true;
  591. return;
  592. }
  593. atmosphere.ProcessingPaused = false;
  594. atmosphere.State = AtmosphereProcessingState.Revalidate;
  595. // We reached the end of this atmosphere's update tick. Break out of the switch.
  596. break;
  597. }
  598. // And increase the update counter.
  599. atmosphere.UpdateCounter++;
  600. }
  601. // We finished processing all atmospheres successfully, therefore we won't be paused next tick.
  602. _simulationPaused = false;
  603. }
  604. }
  605. public enum AtmosphereProcessingState : byte
  606. {
  607. Revalidate,
  608. TileEqualize,
  609. ActiveTiles,
  610. ExcitedGroups,
  611. HighPressureDelta,
  612. Hotspots,
  613. Superconductivity,
  614. PipeNet,
  615. AtmosDevices,
  616. NumStates
  617. }
  618. }