--- /dev/null
+using Content.Server.Administration;
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Administration;
+using Content.Shared.Atmos.Components;
+using Robust.Shared.Console;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+
+namespace Content.Server.Atmos.Commands;
+
+[AdminCommand(AdminFlags.Debug)]
+public sealed class SubstepAtmosCommand : LocalizedEntityCommands
+{
+ [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
+
+ public override string Command => "substepatmos";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ var grid = default(EntityUid);
+
+ switch (args.Length)
+ {
+ case 0:
+ if (!EntityManager.TryGetComponent<TransformComponent>(shell.Player?.AttachedEntity,
+ out var playerxform) ||
+ playerxform.GridUid == null)
+ {
+ shell.WriteError(Loc.GetString("cmd-error-no-grid-provided-or-invalid-grid"));
+ return;
+ }
+
+ grid = playerxform.GridUid.Value;
+ break;
+ case 1:
+ if (!EntityUid.TryParse(args[0], out var parsedGrid) || !EntityManager.EntityExists(parsedGrid))
+ {
+ shell.WriteError(Loc.GetString("cmd-error-couldnt-parse-entity"));
+ return;
+ }
+
+ grid = parsedGrid;
+ break;
+ }
+
+ // i'm straight piratesoftwaremaxxing
+ if (!EntityManager.TryGetComponent<GridAtmosphereComponent>(grid, out var gridAtmos))
+ {
+ shell.WriteError(Loc.GetString("cmd-error-no-gridatmosphere"));
+ return;
+ }
+
+ if (!EntityManager.TryGetComponent<GasTileOverlayComponent>(grid, out var gasTile))
+ {
+ shell.WriteError(Loc.GetString("cmd-error-no-gastileoverlay"));
+ return;
+ }
+
+ if (!EntityManager.TryGetComponent<MapGridComponent>(grid, out var mapGrid))
+ {
+ shell.WriteError(Loc.GetString("cmd-error-no-mapgrid"));
+ return;
+ }
+
+ var xform = EntityManager.GetComponent<TransformComponent>(grid);
+
+ if (xform.MapUid == null || xform.MapID == MapId.Nullspace)
+ {
+ shell.WriteError(Loc.GetString("cmd-error-no-valid-map"));
+ return;
+ }
+
+ var newEnt =
+ new Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>(grid,
+ gridAtmos,
+ gasTile,
+ mapGrid,
+ xform);
+
+ if (gridAtmos.Simulated)
+ {
+ shell.WriteLine(Loc.GetString("cmd-substepatmos-info-implicitly-paused-simulation",
+ ("grid", EntityManager.ToPrettyString(grid))));
+ }
+
+ _atmosphereSystem.SetAtmosphereSimulation(newEnt, false);
+ _atmosphereSystem.RunProcessingFull(newEnt, xform.MapUid.Value, _atmosphereSystem.AtmosTickRate);
+
+ shell.WriteLine(Loc.GetString("cmd-substepatmos-info-substepped-grid", ("grid", EntityManager.ToPrettyString(grid))));
+ }
+
+ public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ if (args.Length == 1)
+ {
+ return CompletionResult.FromHintOptions(
+ CompletionHelper.Components<GridAtmosphereComponent>(args[0], EntityManager),
+ Loc.GetString("cmd-substepatmos-completion-grid-substep"));
+ }
+
+ return CompletionResult.Empty;
+ }
+}
if (atmosphere.LifeStage >= ComponentLifeStage.Stopping || Paused(owner) || !atmosphere.Simulated)
continue;
- atmosphere.Timer += frameTime;
-
- if (atmosphere.Timer < AtmosTime)
- continue;
-
- // We subtract it so it takes lost time into account.
- atmosphere.Timer -= AtmosTime;
-
var map = new Entity<MapAtmosphereComponent?>(xform.MapUid.Value, _mapAtmosQuery.CompOrNull(xform.MapUid.Value));
- switch (atmosphere.State)
+ var completionState = ProcessAtmosphere(ent, map, frameTime);
+
+ switch (completionState)
{
- case AtmosphereProcessingState.Revalidate:
- if (!ProcessRevalidate(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
-
- // Next state depends on whether monstermos equalization is enabled or not.
- // Note: We do this here instead of on the tile equalization step to prevent ending it early.
- // Therefore, a change to this CVar might only be applied after that step is over.
- atmosphere.State = MonstermosEqualization
- ? AtmosphereProcessingState.TileEqualize
- : AtmosphereProcessingState.ActiveTiles;
- continue;
- case AtmosphereProcessingState.TileEqualize:
- if (!ProcessTileEqualize(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.ActiveTiles;
- continue;
- case AtmosphereProcessingState.ActiveTiles:
- if (!ProcessActiveTiles(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- // Next state depends on whether excited groups are enabled or not.
- atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta;
- continue;
- case AtmosphereProcessingState.ExcitedGroups:
- if (!ProcessExcitedGroups(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
- continue;
- case AtmosphereProcessingState.HighPressureDelta:
- if (!ProcessHighPressureDelta((ent, ent)))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = DeltaPressureDamage
- ? AtmosphereProcessingState.DeltaPressure
- : AtmosphereProcessingState.Hotspots;
+ case AtmosphereProcessingCompletionState.Return:
+ return;
+ case AtmosphereProcessingCompletionState.Continue:
continue;
- case AtmosphereProcessingState.DeltaPressure:
- if (!ProcessDeltaPressure(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.Hotspots;
- continue;
- case AtmosphereProcessingState.Hotspots:
- if (!ProcessHotspots(ent))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- // Next state depends on whether superconduction is enabled or not.
- // Note: We do this here instead of on the tile equalization step to prevent ending it early.
- // Therefore, a change to this CVar might only be applied after that step is over.
- atmosphere.State = Superconduction
- ? AtmosphereProcessingState.Superconductivity
- : AtmosphereProcessingState.PipeNet;
- continue;
- case AtmosphereProcessingState.Superconductivity:
- if (!ProcessSuperconductivity(atmosphere))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.PipeNet;
- continue;
- case AtmosphereProcessingState.PipeNet:
- if (!ProcessPipeNets(atmosphere))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.AtmosDevices;
- continue;
- case AtmosphereProcessingState.AtmosDevices:
- if (!ProcessAtmosDevices(ent, map))
- {
- atmosphere.ProcessingPaused = true;
- return;
- }
-
- atmosphere.ProcessingPaused = false;
- atmosphere.State = AtmosphereProcessingState.Revalidate;
-
- // We reached the end of this atmosphere's update tick. Break out of the switch.
+ case AtmosphereProcessingCompletionState.Finished:
break;
}
-
- // And increase the update counter.
- atmosphere.UpdateCounter++;
}
// We finished processing all atmospheres successfully, therefore we won't be paused next tick.
_simulationPaused = false;
}
+
+ /// <summary>
+ /// Processes a <see cref="GridAtmosphereComponent"/> through its processing stages.
+ /// </summary>
+ /// <param name="ent">The entity to process.</param>
+ /// <param name="mapAtmosphere">The <see cref="MapAtmosphereComponent"/> belonging to the
+ /// <see cref="GridAtmosphereComponent"/>'s map.</param>
+ /// <param name="frameTime">The elapsed time since the last frame.</param>
+ /// <returns>An <see cref="AtmosphereProcessingCompletionState"/> that represents the completion state.</returns>
+ private AtmosphereProcessingCompletionState ProcessAtmosphere(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
+ Entity<MapAtmosphereComponent?> mapAtmosphere,
+ float frameTime)
+ {
+ // They call me the deconstructor the way i be deconstructing it
+ // and by it, i mean... my entity
+ var (owner, atmosphere, visuals, grid, xform) = ent;
+
+ atmosphere.Timer += frameTime;
+
+ if (atmosphere.Timer < AtmosTime)
+ return AtmosphereProcessingCompletionState.Continue;
+
+ // We subtract it so it takes lost time into account.
+ atmosphere.Timer -= AtmosTime;
+
+ switch (atmosphere.State)
+ {
+ case AtmosphereProcessingState.Revalidate:
+ if (!ProcessRevalidate(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+
+ // Next state depends on whether monstermos equalization is enabled or not.
+ // Note: We do this here instead of on the tile equalization step to prevent ending it early.
+ // Therefore, a change to this CVar might only be applied after that step is over.
+ atmosphere.State = MonstermosEqualization
+ ? AtmosphereProcessingState.TileEqualize
+ : AtmosphereProcessingState.ActiveTiles;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.TileEqualize:
+ if (!ProcessTileEqualize(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.ActiveTiles;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.ActiveTiles:
+ if (!ProcessActiveTiles(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ // Next state depends on whether excited groups are enabled or not.
+ atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.ExcitedGroups:
+ if (!ProcessExcitedGroups(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.HighPressureDelta:
+ if (!ProcessHighPressureDelta((ent, ent)))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = DeltaPressureDamage
+ ? AtmosphereProcessingState.DeltaPressure
+ : AtmosphereProcessingState.Hotspots;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.DeltaPressure:
+ if (!ProcessDeltaPressure(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.Hotspots;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.Hotspots:
+ if (!ProcessHotspots(ent))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ // Next state depends on whether superconduction is enabled or not.
+ // Note: We do this here instead of on the tile equalization step to prevent ending it early.
+ // Therefore, a change to this CVar might only be applied after that step is over.
+ atmosphere.State = Superconduction
+ ? AtmosphereProcessingState.Superconductivity
+ : AtmosphereProcessingState.PipeNet;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.Superconductivity:
+ if (!ProcessSuperconductivity(atmosphere))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.PipeNet;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.PipeNet:
+ if (!ProcessPipeNets(atmosphere))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.AtmosDevices;
+ return AtmosphereProcessingCompletionState.Continue;
+ case AtmosphereProcessingState.AtmosDevices:
+ if (!ProcessAtmosDevices(ent, mapAtmosphere))
+ {
+ atmosphere.ProcessingPaused = true;
+ return AtmosphereProcessingCompletionState.Return;
+ }
+
+ atmosphere.ProcessingPaused = false;
+ atmosphere.State = AtmosphereProcessingState.Revalidate;
+
+ // We reached the end of this atmosphere's update tick. Break out of the switch.
+ break;
+ }
+
+ atmosphere.UpdateCounter++;
+
+ return AtmosphereProcessingCompletionState.Finished;
+ }
+ }
+
+ /// <summary>
+ /// An enum representing the completion state of a <see cref="GridAtmosphereComponent"/>'s processing steps.
+ /// The processing of a <see cref="GridAtmosphereComponent"/> spans over multiple stages and sticks,
+ /// with the method handling the processing having multiple return types.
+ /// </summary>
+ public enum AtmosphereProcessingCompletionState : byte
+ {
+ /// <summary>
+ /// Method is returning, ex. due to delegating processing to the next tick.
+ /// </summary>
+ Return,
+
+ /// <summary>
+ /// Method is continuing, ex. due to finishing a single processing stage.
+ /// </summary>
+ Continue,
+
+ /// <summary>
+ /// Method is finished with the GridAtmosphere.
+ /// </summary>
+ Finished,
}
public enum AtmosphereProcessingState : byte