diff --git a/Content.Server/Atmos/FireEvent.cs b/Content.Server/Atmos/FireEvent.cs new file mode 100644 index 0000000000..8c1a48abb2 --- /dev/null +++ b/Content.Server/Atmos/FireEvent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.Atmos +{ + public class FireActEvent : EntitySystemMessage + { + public float Temperature { get; } + public float Volume { get; } + + public FireActEvent(float temperature, float volume) + { + Temperature = temperature; + Volume = volume; + } + } +} diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index 43665b6715..ddd834eceb 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -129,17 +129,21 @@ namespace Content.Server.Atmos [ViewVariables] public float Volume { get; set; } - public GasMixture() + public GasMixture() : this(null) { - _atmosphereSystem = EntitySystem.Get(); } - public GasMixture(float volume) + public GasMixture(AtmosphereSystem? atmosphereSystem) + { + _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get(); + } + + public GasMixture(float volume, AtmosphereSystem? atmosphereSystem = null) { if (volume < 0) volume = 0; Volume = volume; - _atmosphereSystem = EntitySystem.Get(); + _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -224,12 +228,12 @@ namespace Content.Server.Atmos public GasMixture RemoveRatio(float ratio) { if(ratio <= 0) - return new GasMixture(Volume); + return new GasMixture(Volume, _atmosphereSystem); if (ratio > 1) ratio = 1; - var removed = new GasMixture {Volume = Volume, Temperature = Temperature}; + var removed = new GasMixture(_atmosphereSystem) {Volume = Volume, Temperature = Temperature}; for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { @@ -252,7 +256,7 @@ namespace Content.Server.Atmos public void CopyFromMutable(GasMixture sample) { if (Immutable) return; - sample._moles.AsSpan().CopyTo(_moles.AsSpan()); + sample._moles.CopyTo(_moles, 0); Temperature = sample.Temperature; } @@ -485,8 +489,7 @@ namespace Content.Server.Atmos var temperature = Temperature; var energy = ThermalEnergy; - // TODO ATMOS Take reaction priority into account! - foreach (var prototype in IoCManager.Resolve().EnumeratePrototypes()) + foreach (var prototype in _atmosphereSystem.GasReactions) { if (energy < prototype.MinimumEnergyRequirement || temperature < prototype.MinimumTemperatureRequirement) @@ -508,7 +511,7 @@ namespace Content.Server.Atmos if (!doReaction) continue; - reaction = prototype.React(this, holder); + reaction = prototype.React(this, holder, _atmosphereSystem.EventBus); if(reaction.HasFlag(ReactionResult.StopReactions)) break; } @@ -588,7 +591,7 @@ namespace Content.Server.Atmos public object Clone() { - var newMixture = new GasMixture() + var newMixture = new GasMixture(_atmosphereSystem) { _moles = (float[])_moles.Clone(), _molesArchived = (float[])_molesArchived.Clone(), diff --git a/Content.Server/Atmos/HighPressureMovementController.cs b/Content.Server/Atmos/HighPressureMovementController.cs index bd9f6b3e38..133d141fb0 100644 --- a/Content.Server/Atmos/HighPressureMovementController.cs +++ b/Content.Server/Atmos/HighPressureMovementController.cs @@ -1,6 +1,7 @@ #nullable enable using System; using Content.Server.GameObjects.Components.Atmos; +using Content.Shared.Atmos; using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.Physics; using Robust.Shared.Interfaces.Random; @@ -24,7 +25,7 @@ namespace Content.Server.Atmos private const float ProbabilityBasePercent = 10f; private const float ThrowForce = 100f; - public void ExperiencePressureDifference(int cycle, float pressureDifference, Direction direction, + public void ExperiencePressureDifference(int cycle, float pressureDifference, AtmosDirection direction, float pressureResistanceProbDelta, GridCoordinates throwTarget) { if (ControlledComponent == null) @@ -54,14 +55,14 @@ namespace Content.Server.Atmos if (throwTarget != GridCoordinates.InvalidGrid) { var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f; - var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToVec()).Normalized; + var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToDirection().ToVec()).Normalized; LinearVelocity = pos * moveForce; } else { var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f); - LinearVelocity = direction.ToVec() * moveForce; + LinearVelocity = direction.ToDirection().ToVec() * moveForce; } pressureComponent.LastHighPressureMovementAirCycle = cycle; diff --git a/Content.Server/Atmos/IGridAtmosphereComponent.cs b/Content.Server/Atmos/IGridAtmosphereComponent.cs index 416445b1a9..1b53d33610 100644 --- a/Content.Server/Atmos/IGridAtmosphereComponent.cs +++ b/Content.Server/Atmos/IGridAtmosphereComponent.cs @@ -13,11 +13,6 @@ namespace Content.Server.Atmos /// int UpdateCounter { get; } - /// - /// How many tiles have high pressure delta. - /// - int HighPressureDeltaCount { get; } - /// /// Control variable for equalization. /// @@ -115,14 +110,14 @@ namespace Content.Server.Atmos /// /// /// - TileAtmosphere GetTile(MapIndices indices); + TileAtmosphere GetTile(MapIndices indices, bool createSpace = true); /// /// Returns a tile. /// /// /// - TileAtmosphere GetTile(GridCoordinates coordinates); + TileAtmosphere GetTile(GridCoordinates coordinates, bool createSpace = true); /// /// Returns if the tile in question is air-blocked. diff --git a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs index e0dffe25c6..732a39fce4 100644 --- a/Content.Server/Atmos/Reactions/GasReactionPrototype.cs +++ b/Content.Server/Atmos/Reactions/GasReactionPrototype.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Content.Server.Interfaces; using Content.Shared.Atmos; +using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using YamlDotNet.RepresentationModel; @@ -64,13 +65,13 @@ namespace Content.Server.Atmos.Reactions serializer.DataField(ref _effects, "effects", new List()); } - public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder) + public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder, IEventBus eventBus) { var result = ReactionResult.NoReaction; foreach (var effect in _effects) { - result |= effect.React(mixture, holder); + result |= effect.React(mixture, holder, eventBus); } return result; diff --git a/Content.Server/Atmos/Reactions/PhoronFireReaction.cs b/Content.Server/Atmos/Reactions/PhoronFireReaction.cs index 46e8ceec2b..1aaf80a0d6 100644 --- a/Content.Server/Atmos/Reactions/PhoronFireReaction.cs +++ b/Content.Server/Atmos/Reactions/PhoronFireReaction.cs @@ -1,8 +1,12 @@ #nullable enable using System; using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Shared.Atmos; +using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; using Robust.Shared.Serialization; namespace Content.Server.Atmos.Reactions @@ -10,7 +14,7 @@ namespace Content.Server.Atmos.Reactions [UsedImplicitly] public class PhoronFireReaction : IGasReactionEffect { - public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder) + public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus) { var energyReleased = 0f; var oldHeatCapacity = mixture.HeatCapacity; @@ -71,9 +75,7 @@ namespace Content.Server.Atmos.Reactions { location.HotspotExpose(temperature, mixture.Volume); - // TODO ATMOS Expose temperature all items on cell - - location.TemperatureExpose(mixture, temperature, mixture.Volume); + eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume)); } } diff --git a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs index 8b90431253..da77173710 100644 --- a/Content.Server/Atmos/Reactions/TritiumFireReaction.cs +++ b/Content.Server/Atmos/Reactions/TritiumFireReaction.cs @@ -1,7 +1,10 @@ #nullable enable using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Shared.Atmos; +using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Shared.GameObjects; using Robust.Shared.Serialization; namespace Content.Server.Atmos.Reactions @@ -13,7 +16,7 @@ namespace Content.Server.Atmos.Reactions { } - public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder) + public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus) { var energyReleased = 0f; var oldHeatCapacity = mixture.HeatCapacity; @@ -66,9 +69,7 @@ namespace Content.Server.Atmos.Reactions { location.HotspotExpose(temperature, mixture.Volume); - // TODO ATMOS Expose temperature all items on cell - - location.TemperatureExpose(mixture, temperature, mixture.Volume); + eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume)); } } diff --git a/Content.Server/Atmos/TemperatureExposeEvent.cs b/Content.Server/Atmos/TemperatureExposeEvent.cs new file mode 100644 index 0000000000..5401fe0279 --- /dev/null +++ b/Content.Server/Atmos/TemperatureExposeEvent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.Server.Atmos +{ + public class TemperatureExposeEvent : EntitySystemMessage + { + public MapIndices Indices { get; } + public GridId Grid { get; } + public GasMixture Air { get; } + public float Temperature { get; } + public float Volume { get; } + + public TemperatureExposeEvent(MapIndices indices, GridId gridId, GasMixture air, float temperature, float volume) + { + Indices = indices; + Grid = gridId; + Air = air; + Temperature = temperature; + Volume = volume; + } + } +} diff --git a/Content.Server/Atmos/TileAtmosInfo.cs b/Content.Server/Atmos/TileAtmosInfo.cs index 43ed5f129d..23cc37e550 100644 --- a/Content.Server/Atmos/TileAtmosInfo.cs +++ b/Content.Server/Atmos/TileAtmosInfo.cs @@ -1,4 +1,5 @@ using System; +using Content.Shared.Atmos; using Robust.Shared.Maths; using Robust.Shared.ViewVariables; @@ -30,15 +31,15 @@ namespace Content.Server.Atmos [ViewVariables] public float TransferDirectionSouth; - public float this[Direction direction] + public float this[AtmosDirection direction] { get => direction switch { - Direction.East => TransferDirectionEast, - Direction.West => TransferDirectionWest, - Direction.North => TransferDirectionNorth, - Direction.South => TransferDirectionSouth, + AtmosDirection.East => TransferDirectionEast, + AtmosDirection.West => TransferDirectionWest, + AtmosDirection.North => TransferDirectionNorth, + AtmosDirection.South => TransferDirectionSouth, _ => throw new ArgumentOutOfRangeException(nameof(direction)) }; @@ -46,16 +47,16 @@ namespace Content.Server.Atmos { switch (direction) { - case Direction.East: + case AtmosDirection.East: TransferDirectionEast = value; break; - case Direction.West: + case AtmosDirection.West: TransferDirectionWest = value; break; - case Direction.North: + case AtmosDirection.North: TransferDirectionNorth = value; break; - case Direction.South: + case AtmosDirection.South: TransferDirectionSouth = value; break; default: @@ -64,10 +65,16 @@ namespace Content.Server.Atmos } } + public float this[int index] + { + get => this[(AtmosDirection) (1 << index)]; + set => this[(AtmosDirection) (1 << index)] = value; + } + [ViewVariables] public float CurrentTransferAmount; - public Direction CurrentTransferDirection; + public AtmosDirection CurrentTransferDirection; [ViewVariables] public bool FastDone; diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index 6f93d0c9a0..a8afc84cb5 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Content.Server.Atmos.Reactions; using Content.Server.GameObjects.Components.Atmos; -using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems.Atmos; using Content.Server.Interfaces; using Content.Shared.Atmos; @@ -12,13 +11,13 @@ using Content.Shared.Audio; using Content.Shared.Maps; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.Containers; +using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Random; using Robust.Shared.IoC; -using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Random; @@ -28,18 +27,15 @@ namespace Content.Server.Atmos { public class TileAtmosphere : IGasMixtureHolder { - [Robust.Shared.IoC.Dependency] private IRobustRandom _robustRandom = default!; - [Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!; - [Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!; + [Robust.Shared.IoC.Dependency] private readonly IRobustRandom _robustRandom = default!; + [Robust.Shared.IoC.Dependency] private readonly IEntityManager _entityManager = default!; + [Robust.Shared.IoC.Dependency] private readonly IMapManager _mapManager = default!; - private static readonly TileAtmosphereComparer _comparer = new TileAtmosphereComparer(); + private static readonly TileAtmosphereComparer Comparer = new TileAtmosphereComparer(); - [ViewVariables] - private int _archivedCycle = 0; - - [ViewVariables] - private int _currentCycle = 0; + [ViewVariables] private int _archivedCycle; + [ViewVariables] private int _currentCycle; [ViewVariables] private static GasTileOverlaySystem _gasTileOverlaySystem; @@ -51,13 +47,13 @@ namespace Content.Server.Atmos private float _temperatureArchived = Atmospherics.T20C; // I know this being static is evil, but I seriously can't come up with a better solution to sound spam. - private static int _soundCooldown = 0; + private static int _soundCooldown; [ViewVariables] - public TileAtmosphere PressureSpecificTarget { get; set; } = null; + public TileAtmosphere PressureSpecificTarget { get; set; } [ViewVariables] - public float PressureDifference { get; set; } = 0; + public float PressureDifference { get; set; } [ViewVariables(VVAccess.ReadWrite)] public float HeatCapacity { get; set; } = 1f; @@ -66,13 +62,19 @@ namespace Content.Server.Atmos public float ThermalConductivity => Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.05f; [ViewVariables] - public bool Excited { get; set; } = false; + public bool Excited { get; set; } [ViewVariables] - private GridAtmosphereComponent _gridAtmosphereComponent; + private readonly GridAtmosphereComponent _gridAtmosphereComponent; + + /// + /// Adjacent tiles in the same order as . (NSEW) + /// + [ViewVariables] + private readonly TileAtmosphere[] _adjacentTiles = new TileAtmosphere[Atmospherics.Directions]; [ViewVariables] - private readonly Dictionary _adjacentTiles = new Dictionary(); + private AtmosDirection _adjacentBits = AtmosDirection.Invalid; [ViewVariables] private TileAtmosInfo _tileAtmosInfo; @@ -80,7 +82,7 @@ namespace Content.Server.Atmos [ViewVariables] public Hotspot Hotspot; - private Direction _pressureDirection; + private AtmosDirection _pressureDirection; [ViewVariables] public GridId GridIndex { get; } @@ -100,14 +102,16 @@ namespace Content.Server.Atmos [ViewVariables] public bool BlocksAir => _gridAtmosphereComponent.IsAirBlocked(GridIndices); - public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, MapIndices gridIndices, GasMixture mixture = null) + public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, MapIndices gridIndices, GasMixture mixture = null, bool immutable = false) { IoCManager.InjectDependencies(this); _gridAtmosphereComponent = atmosphereComponent; GridIndex = gridIndex; GridIndices = gridIndices; Air = mixture; - ResetTileAtmosInfo(); + + if(immutable) + Air?.MarkImmutable(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -163,7 +167,7 @@ namespace Content.Server.Atmos } } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void HighPressureMovements() { // TODO ATMOS finish this @@ -175,7 +179,6 @@ namespace Content.Server.Atmos GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(PressureDifference / 10, 10, 100))); } - foreach (var entity in _entityManager.GetEntitiesIntersecting(_mapManager.GetGrid(GridIndex).ParentMapId, Box2.UnitCentered.Translated(GridIndices))) { if (!entity.TryGetComponent(out ICollidableComponent physics) @@ -195,7 +198,7 @@ namespace Content.Server.Atmos if (PressureDifference > 100) { - // Do space wind graphics here! + // TODO ATMOS Do space wind graphics here! } _soundCooldown++; @@ -220,20 +223,22 @@ namespace Content.Server.Atmos } } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EqualizePressureInZone(int cycleNum) { if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done. - ResetTileAtmosInfo(); - + _tileAtmosInfo = new TileAtmosInfo(); var startingMoles = Air.TotalMoles; var runAtmos = false; // We need to figure if this is necessary - foreach (var (direction, other) in _adjacentTiles) + for (var i = 0; i < Atmospherics.Directions; i++) { + var direction = (AtmosDirection) (1 << i); + if (!_adjacentBits.HasFlag(direction)) continue; + var other = _adjacentTiles[i]; if (other?.Air == null) continue; var comparisonMoles = other.Air.TotalMoles; if (!(MathF.Abs(comparisonMoles - startingMoles) > Atmospherics.MinimumMolesDeltaToMove)) continue; @@ -265,13 +270,15 @@ namespace Content.Server.Atmos totalMoles += tileMoles; } - foreach (var (_, adj) in exploring._adjacentTiles) + for (var j = 0; j < Atmospherics.Directions; j++) { + var direction = (AtmosDirection) (1 << j); + if (!exploring._adjacentBits.HasFlag(direction)) continue; + var adj = exploring._adjacentTiles[j]; if (adj?.Air == null) continue; if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue; - adj.ResetTileAtmosInfo(); + adj._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle}; - adj._tileAtmosInfo.LastQueueCycle = queueCycle; if(tileCount < Atmospherics.ZumosHardTileLimit) tiles[tileCount++] = adj; if (adj.Air.Immutable) @@ -326,47 +333,42 @@ namespace Content.Server.Atmos if (giverTilesLength > logN && takerTilesLength > logN) { // Even if it fails, it will speed up the next part. - Array.Sort(tiles, 0, tileCount, _comparer); + Array.Sort(tiles, 0, tileCount, Comparer); for (var i = 0; i < tileCount; i++) { var tile = tiles[i]; tile._tileAtmosInfo.FastDone = true; if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue; - var eligibleDirections = ArrayPool.Shared.Rent(4); + var eligibleDirections = AtmosDirection.Invalid; var eligibleDirectionCount = 0; - foreach (var direction in Cardinal) + for (var j = 0; j < Atmospherics.Directions; j++) { - if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; + var direction = (AtmosDirection) (1 << j); + if (!tile._adjacentBits.HasFlag(direction)) continue; + var tile2 = tile._adjacentTiles[j]; // skip anything that isn't part of our current processing block. if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue; - eligibleDirections[eligibleDirectionCount++] = direction; + eligibleDirections |= direction; + eligibleDirectionCount++; } if (eligibleDirectionCount <= 0) continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this. var molesToMove = tile._tileAtmosInfo.MoleDelta / eligibleDirectionCount; - foreach (var direction in Cardinal) + for (var j = 0; j < Atmospherics.Directions; j++) { - var hasDirection = false; - for (var j = 0; j < eligibleDirectionCount; j++) - { - if (eligibleDirections[j] != direction) continue; - hasDirection = true; - break; - } + var direction = (AtmosDirection) (1 << j); + if (!eligibleDirections.HasFlag(direction)) continue; - if (hasDirection || !tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; tile.AdjustEqMovement(direction, molesToMove); tile._tileAtmosInfo.MoleDelta -= molesToMove; - tile2._tileAtmosInfo.MoleDelta += molesToMove; + tile._adjacentTiles[j]._tileAtmosInfo.MoleDelta += molesToMove; } - - ArrayPool.Shared.Return(eligibleDirections); } giverTilesLength = 0; @@ -393,7 +395,7 @@ namespace Content.Server.Atmos for (var j = 0; j < giverTilesLength; j++) { var giver = giverTiles[j]; - giver._tileAtmosInfo.CurrentTransferDirection = (Direction) (-1); + giver._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid; giver._tileAtmosInfo.CurrentTransferAmount = 0; var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl; var queueLength = 0; @@ -405,9 +407,11 @@ namespace Content.Server.Atmos break; // We're done here now. Let's not do more work than needed. var tile = queue[i]; - foreach (var direction in Cardinal) + for (var k = 0; k < Atmospherics.Directions; k++) { - if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; + var direction = (AtmosDirection) (1 << k); + if (!tile._adjacentBits.HasFlag(direction)) continue; + var tile2 = tile._adjacentTiles[k]; if (giver._tileAtmosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed. if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue; if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue; @@ -441,15 +445,11 @@ namespace Content.Server.Atmos for (var i = queueLength - 1; i >= 0; i--) { var tile = queue[i]; - if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && - tile._tileAtmosInfo.CurrentTransferDirection != (Direction) (-1)) + if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && tile._tileAtmosInfo.CurrentTransferDirection != AtmosDirection.Invalid) { - tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, - tile._tileAtmosInfo.CurrentTransferAmount); - if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, - out var adjacent)) - adjacent._tileAtmosInfo.CurrentTransferAmount += - tile._tileAtmosInfo.CurrentTransferAmount; + tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount); + tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()] + ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount; tile._tileAtmosInfo.CurrentTransferAmount = 0; } } @@ -463,7 +463,7 @@ namespace Content.Server.Atmos for (var j = 0; j < takerTilesLength; j++) { var taker = takerTiles[j]; - taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; + taker._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid; taker._tileAtmosInfo.CurrentTransferAmount = 0; var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl; var queueLength = 0; @@ -475,10 +475,11 @@ namespace Content.Server.Atmos break; // We're done here now. Let's not do more work than needed. var tile = queue[i]; - foreach (var direction in Cardinal) + for (var k = 0; k < Atmospherics.Directions; k++) { - if (!tile._adjacentTiles.ContainsKey(direction)) continue; - var tile2 = tile._adjacentTiles[direction]; + var direction = (AtmosDirection) (1 << k); + if (!tile._adjacentBits.HasFlag(direction)) continue; + var tile2 = tile._adjacentTiles[k]; if (taker._tileAtmosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed. if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue; @@ -512,16 +513,14 @@ namespace Content.Server.Atmos for (var i = queueLength - 1; i >= 0; i--) { var tile = queue[i]; - if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) + if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue; tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount); - if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent)) - { - adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount; - tile._tileAtmosInfo.CurrentTransferAmount = 0; - } + tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()] + ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount; + tile._tileAtmosInfo.CurrentTransferAmount = 0; } } @@ -537,9 +536,11 @@ namespace Content.Server.Atmos for (var i = 0; i < tileCount; i++) { var tile = tiles[i]; - foreach (var direction in Cardinal) + for (var j = 0; j < Atmospherics.Directions; j++) { - if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; + var direction = (AtmosDirection) (1 << j); + if (!tile._adjacentBits.HasFlag(direction)) continue; + var tile2 = tile._adjacentTiles[j]; if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange) continue; _gridAtmosphereComponent.AddActiveTile(tile2); break; @@ -555,69 +556,68 @@ namespace Content.Server.Atmos [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FinalizeEq() { - var transferDirections = new Dictionary(); + var transferDirections = new float[Atmospherics.Directions]; var hasTransferDirs = false; - foreach (var direction in Cardinal) + for (var i = 0; i < Atmospherics.Directions; i++) { - var amount = _tileAtmosInfo[direction]; + var amount = _tileAtmosInfo[i]; if (amount == 0) continue; - transferDirections[direction] = amount; - _tileAtmosInfo[direction] = 0; + transferDirections[i] = amount; + _tileAtmosInfo[i] = 0; // Set them to 0 to prevent infinite recursion. hasTransferDirs = true; } if (!hasTransferDirs) return; - foreach (var (direction, amount) in transferDirections) + for(var i = 0; i < Atmospherics.Directions; i++) { - if (!_adjacentTiles.TryGetValue(direction, out var tile) || tile.Air == null) continue; + var direction = (AtmosDirection) (1 << i); + if (!_adjacentBits.HasFlag(direction)) continue; + var amount = transferDirections[i]; + var tile = _adjacentTiles[i]; + if (tile?.Air == null) continue; if (amount > 0) { if (Air.TotalMoles < amount) - FinalizeEqNeighbors(transferDirections.Keys); + FinalizeEqNeighbors(transferDirections); + tile._tileAtmosInfo[direction.GetOpposite()] = 0; tile.Air.Merge(Air.Remove(amount)); UpdateVisuals(); tile.UpdateVisuals(); - ConsiderPressureDifference(direction, amount); + ConsiderPressureDifference(tile, amount); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FinalizeEqNeighbors(IEnumerable directions) + private void FinalizeEqNeighbors(in float[] transferDirs) { - foreach (var direction in directions) + for (var i = 0; i < Atmospherics.Directions; i++) { - var amount = _tileAtmosInfo[direction]; - if(amount < 0 && _adjacentTiles.TryGetValue(direction, out var adjacent)) - adjacent.FinalizeEq(); + var direction = (AtmosDirection) (1 << i); + var amount = transferDirs[i]; + if(amount < 0 && _adjacentBits.HasFlag(direction)) + _adjacentTiles[i].FinalizeEq(); // A bit of recursion if needed. } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ConsiderPressureDifference(Direction direction, float difference) + private void ConsiderPressureDifference(TileAtmosphere other, float difference) { _gridAtmosphereComponent.AddHighPressureDelta(this); if (difference > PressureDifference) { PressureDifference = difference; - _pressureDirection = difference < 0 ? direction.GetOpposite() : direction; + _pressureDirection = ((Vector2i)(GridIndices - other.GridIndices)).GetDir().ToAtmosDirection(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AdjustEqMovement(Direction direction, float molesToMove) + private void AdjustEqMovement(AtmosDirection direction, float amount) { - _tileAtmosInfo[direction] += molesToMove; - if(direction != Direction.Invalid && _adjacentTiles.TryGetValue(direction, out var adj)) - adj._tileAtmosInfo[direction.GetOpposite()] -= molesToMove; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ResetTileAtmosInfo() - { - _tileAtmosInfo = new TileAtmosInfo {CurrentTransferDirection = Direction.Invalid}; + _tileAtmosInfo[direction] += amount; + _adjacentTiles[direction.ToIndex()]._tileAtmosInfo[direction.GetOpposite()] -= amount; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -635,9 +635,13 @@ namespace Content.Server.Atmos _currentCycle = fireCount; var adjacentTileLength = 0; - foreach (var (direction, enemyTile) in _adjacentTiles) + for(var i = 0; i < Atmospherics.Directions; i++) { - // If the tile is null or has no air, we don't do anything + var direction = (AtmosDirection) (1 << i); + if (!_adjacentBits.HasFlag(direction)) continue; + var enemyTile = _adjacentTiles[i]; + + // If the tile is null or has no air, we don't do anything for it. if(enemyTile?.Air == null) continue; adjacentTileLength++; if (fireCount <= enemyTile._currentCycle) continue; @@ -685,11 +689,11 @@ namespace Content.Server.Atmos // Space wind! if (difference > 0) { - ConsiderPressureDifference(direction, difference); + ConsiderPressureDifference(enemyTile, difference); } else { - enemyTile.ConsiderPressureDifference(direction.GetOpposite(), -difference); + enemyTile.ConsiderPressureDifference(this, -difference); } LastShareCheck(); @@ -737,7 +741,7 @@ namespace Content.Server.Atmos if (Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread) { var radiatedTemperature = Air.Temperature * Atmospherics.FireSpreadRadiosityScale; - foreach (var (_, tile) in _adjacentTiles) + foreach (var tile in _adjacentTiles) { if(!tile.Hotspot.Valid) tile.HotspotExpose(radiatedTemperature, Atmospherics.CellVolume/4); @@ -771,17 +775,19 @@ namespace Content.Server.Atmos else { var affected = Air.RemoveRatio(Hotspot.Volume / Air.Volume); - if (affected != null) - { - affected.Temperature = Hotspot.Temperature; - affected.React(this); - Hotspot.Temperature = affected.Temperature; - Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate; - AssumeAir(affected); - } + affected.Temperature = Hotspot.Temperature; + affected.React(this); + Hotspot.Temperature = affected.Temperature; + Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate; + AssumeAir(affected); } - // TODO ATMOS Let all entities in this tile know about the fire? + var tileRef = GridIndices.GetTileRef(GridIndex); + + if (tileRef == null) return; + + _gridAtmosphereComponent.Owner.EntityManager. + EventBus.QueueEvent(EventSource.Local, new FireActEvent(Hotspot.Temperature, Hotspot.Volume)); } private bool ConsiderSuperconductivity() @@ -806,24 +812,24 @@ namespace Content.Server.Atmos public void Superconduct() { var directions = ConductivityDirections(); - var adjacentTiles = _gridAtmosphereComponent.GetAdjacentTiles(GridIndices, true); - if (directions.Length > 0) + for(var i = 0; i < Atmospherics.Directions; i++) { - foreach (var direction in directions) - { - if (!adjacentTiles.TryGetValue(direction, out var adjacent)) continue; + var direction = (AtmosDirection) (1 << i); + if (!directions.HasFlag(direction)) continue; - if (adjacent.ThermalConductivity == 0f) - continue; + var adjacent = _adjacentTiles[direction.ToIndex()]; - if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter) - adjacent.Archive(_gridAtmosphereComponent.UpdateCounter); + // TODO ATMOS handle adjacent being null. + if (adjacent == null || adjacent.ThermalConductivity == 0f) + continue; - adjacent.NeighborConductWithSource(this); + if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter) + adjacent.Archive(_gridAtmosphereComponent.UpdateCounter); - adjacent.ConsiderSuperconductivity(); - } + adjacent.NeighborConductWithSource(this); + + adjacent.ConsiderSuperconductivity(); } RadiateToSpace(); @@ -917,20 +923,20 @@ namespace Content.Server.Atmos } } - public Direction[] ConductivityDirections() + public AtmosDirection ConductivityDirections() { if(BlocksAir) { if(_archivedCycle < _gridAtmosphereComponent.UpdateCounter) Archive(_gridAtmosphereComponent.UpdateCounter); - return Cardinal; + return AtmosDirection.All; } // TODO ATMOS check if this is correct - return Cardinal; + return AtmosDirection.All; } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ExplosivelyDepressurize(int cycleNum) { if (Air == null) return; @@ -947,14 +953,13 @@ namespace Content.Server.Atmos tiles[tileCount++] = this; - ResetTileAtmosInfo(); - _tileAtmosInfo.LastQueueCycle = queueCycle; + _tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle}; for (var i = 0; i < tileCount; i++) { var tile = tiles[i]; tile._tileAtmosInfo.LastCycle = cycleNum; - tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; + tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid; if (tile.Air.Immutable) { spaceTiles[spaceTileCount++] = tile; @@ -962,18 +967,19 @@ namespace Content.Server.Atmos } else { - foreach (var direction in Cardinal) + for (var j = 0; j < Atmospherics.Directions; j++) { - if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; + var direction = (AtmosDirection) (1 << j); + if (!tile._adjacentBits.HasFlag(direction)) continue; + var tile2 = tile._adjacentTiles[j]; if (tile2.Air == null) continue; if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue; tile.ConsiderFirelocks(tile2); // The firelocks might have closed on us. - if (tile._adjacentTiles[direction]?.Air == null) continue; - tile2.ResetTileAtmosInfo(); - tile2._tileAtmosInfo.LastQueueCycle = queueCycle; + if (!tile._adjacentBits.HasFlag(direction)) continue; + tile2._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle}; tiles[tileCount++] = tile2; } } @@ -991,18 +997,21 @@ namespace Content.Server.Atmos var tile = spaceTiles[i]; progressionOrder[progressionCount++] = tile; tile._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow; - tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; + tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid; } for (var i = 0; i < progressionCount; i++) { var tile = progressionOrder[i]; - foreach (var direction in Cardinal) + for (var j = 0; j < Atmospherics.Directions; j++) { - if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue; + var direction = (AtmosDirection) (1 << j); + // TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres. + if (!tile._adjacentBits.HasFlag(direction) && !tile.Air.Immutable) continue; + var tile2 = tile._adjacentTiles[j]; if (tile2?._tileAtmosInfo.LastQueueCycle != queueCycle) continue; if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue; - if(tile2.Air.Immutable) continue; + if(tile2.Air?.Immutable ?? false) continue; tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite(); tile2._tileAtmosInfo.CurrentTransferAmount = 0; tile2.PressureSpecificTarget = tile.PressureSpecificTarget; @@ -1014,10 +1023,11 @@ namespace Content.Server.Atmos for (var i = progressionCount - 1; i >= 0; i--) { var tile = progressionOrder[i]; - if (tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue; + if (tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue; _gridAtmosphereComponent.AddHighPressureDelta(tile); _gridAtmosphereComponent.AddActiveTile(tile); - if (!tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var tile2) || tile2.Air == null) continue; + var tile2 = tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]; + if (tile2?.Air == null) continue; var sum = tile2.Air.TotalMoles; totalGasesRemoved += sum; tile._tileAtmosInfo.CurrentTransferAmount += sum; @@ -1025,7 +1035,7 @@ namespace Content.Server.Atmos tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount; tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection; - if (tile2._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) + if (tile2._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid) { tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount; tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection; @@ -1082,21 +1092,30 @@ namespace Content.Server.Atmos [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UpdateAdjacent() { - foreach (var direction in Cardinal) + for (var i = 0; i < Atmospherics.Directions; i++) { - if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction))) + var direction = (AtmosDirection) (1 << i); + + var otherIndices = GridIndices.Offset(direction.ToDirection()); + + var isSpace = _gridAtmosphereComponent.IsSpace(GridIndices); + var adjacent = _gridAtmosphereComponent.GetTile(otherIndices, !isSpace); + _adjacentTiles[direction.ToIndex()] = adjacent; + adjacent?.UpdateAdjacent(direction.GetOpposite()); + + if (adjacent != null && !_gridAtmosphereComponent.IsAirBlocked(adjacent.GridIndices)) { - var adjacent = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction)); - _adjacentTiles[direction] = adjacent; - adjacent.UpdateAdjacent(direction.GetOpposite()); + _adjacentBits |= direction; } } } - public void UpdateAdjacent(Direction direction) + public void UpdateAdjacent(AtmosDirection direction) { - if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction))) - _adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction)); + if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection()))) + { + _adjacentTiles[direction.ToIndex()] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction.ToDirection())); + } } private void LastShareCheck() @@ -1111,13 +1130,7 @@ namespace Content.Server.Atmos } } - private static readonly Direction[] Cardinal = - new Direction[] - { - Direction.North, Direction.East, Direction.South, Direction.West - }; - - public void TemperatureExpose(GasMixture mixture, float temperature, float cellVolume) + public void TemperatureExpose(GasMixture air, float temperature, float volume) { // TODO ATMOS do this } diff --git a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs index 87346d632b..fd56e9d34d 100644 --- a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs @@ -36,7 +36,7 @@ namespace Content.Server.GameObjects.Components.Atmos /// /// Check current execution time every n instances processed. /// - private const int LagCheckIterations = 15; + private const int LagCheckIterations = 30; /// /// Max milliseconds allowed for atmos updates. @@ -50,46 +50,93 @@ namespace Content.Server.GameObjects.Components.Atmos public override string Name => "GridAtmosphere"; + private bool _paused = false; private float _timer = 0f; private Stopwatch _stopwatch = new Stopwatch(); + + [ViewVariables] public int UpdateCounter { get; private set; } = 0; + [ViewVariables] + private double _tileEqualizeLastProcess; + [ViewVariables] private readonly HashSet _excitedGroups = new HashSet(1000); + [ViewVariables] + private int ExcitedGroupCount => _excitedGroups.Count; + + [ViewVariables] + private double _excitedGroupLastProcess; + [ViewVariables] private readonly Dictionary _tiles = new Dictionary(1000); [ViewVariables] private readonly HashSet _activeTiles = new HashSet(1000); + [ViewVariables] + private int ActiveTilesCount => _activeTiles.Count; + + [ViewVariables] + private double _activeTilesLastProcess; + [ViewVariables] private readonly HashSet _hotspotTiles = new HashSet(1000); + [ViewVariables] + private int HotspotTilesCount => _hotspotTiles.Count; + + [ViewVariables] + private double _hotspotsLastProcess; + [ViewVariables] private readonly HashSet _superconductivityTiles = new HashSet(1000); + [ViewVariables] + private int SuperconductivityTilesCount => _superconductivityTiles.Count; + + [ViewVariables] + private double _superconductivityLastProcess; + [ViewVariables] private readonly HashSet _invalidatedCoords = new HashSet(1000); + [ViewVariables] + private int InvalidatedCoordsCount => _invalidatedCoords.Count; + [ViewVariables] private HashSet _highPressureDelta = new HashSet(1000); [ViewVariables] - private readonly List _pipeNets = new List(); - - /// - /// Index of most recently updated . - /// - private int _pipeNetIndex = 0; + private int HighPressureDeltaCount => _highPressureDelta.Count; [ViewVariables] - private readonly List _pipeNetDevices = new List(); + private double _highPressureDeltaLastProcess; - /// - /// Index of most recently updated . - /// - private int _deviceIndex = 0; + [ViewVariables] + private readonly HashSet _pipeNets = new HashSet(); + + [ViewVariables] + private double _pipeNetLastProcess; + + [ViewVariables] + private readonly HashSet _pipeNetDevices = new HashSet(); + + [ViewVariables] + private double _pipeNetDevicesLastProcess; + + [ViewVariables] + private Queue _currentRunTiles = new Queue(); + + [ViewVariables] + private Queue _currentRunExcitedGroups = new Queue(); + + [ViewVariables] + private Queue _currentRunPipeNet = new Queue(); + + [ViewVariables] + private Queue _currentRunPipeNetDevice = new Queue(); [ViewVariables] private ProcessState _state = ProcessState.TileEqualize; @@ -204,9 +251,10 @@ namespace Content.Server.GameObjects.Components.Atmos tile.UpdateAdjacent(); tile.UpdateVisuals(); - foreach (var direction in Cardinal) + for (var i = 0; i < Atmospherics.Directions; i++) { - var otherIndices = indices.Offset(direction); + var direction = (AtmosDirection) (1 << i); + var otherIndices = indices.Offset(direction.ToDirection()); var otherTile = GetTile(otherIndices); AddActiveTile(otherTile); otherTile?.UpdateAdjacent(direction.GetOpposite()); @@ -324,7 +372,6 @@ namespace Content.Server.GameObjects.Components.Atmos public void RemovePipeNet(IPipeNet pipeNet) { _pipeNets.Remove(pipeNet); - _deviceIndex = 0; } public void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice) @@ -335,28 +382,25 @@ namespace Content.Server.GameObjects.Components.Atmos public void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice) { _pipeNetDevices.Remove(pipeNetDevice); - _deviceIndex = 0; } /// - public TileAtmosphere? GetTile(GridCoordinates coordinates) + public TileAtmosphere? GetTile(GridCoordinates coordinates, bool createSpace = true) { - return GetTile(coordinates.ToMapIndices(_mapManager)); + return GetTile(coordinates.ToMapIndices(_mapManager), createSpace); } /// - public TileAtmosphere? GetTile(MapIndices indices) + public TileAtmosphere? GetTile(MapIndices indices, bool createSpace = true) { if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return null; if (_tiles.TryGetValue(indices, out var tile)) return tile; // We don't have that tile! - if (IsSpace(indices)) + if (IsSpace(indices) && createSpace) { - var space = new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(int.MaxValue){Temperature = Atmospherics.TCMB}); - space.Air.MarkImmutable(); - return space; + return new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.TCMB}, true); } return null; @@ -378,23 +422,21 @@ namespace Content.Server.GameObjects.Components.Atmos return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty; } - public Dictionary GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false) + public Dictionary GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false) { - var sides = new Dictionary(); - foreach (var dir in Cardinal) + var sides = new Dictionary(); + for (var i = 0; i < Atmospherics.Directions; i++) { - var side = indices.Offset(dir); + var direction = (AtmosDirection) (1 << i); + var side = indices.Offset(direction.ToDirection()); var tile = GetTile(side); if (tile != null && (tile.Air != null || includeAirBlocked)) - sides[dir] = tile; + sides[direction] = tile; } return sides; } - /// - public int HighPressureDeltaCount => _highPressureDelta.Count; - public long EqualizationQueueCycleControl { get; set; } /// @@ -422,35 +464,83 @@ namespace Content.Server.GameObjects.Components.Atmos switch (_state) { case ProcessState.TileEqualize: - ProcessTileEqualize(); + if (!ProcessTileEqualize(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.ActiveTiles; return; case ProcessState.ActiveTiles: - ProcessActiveTiles(); + if (!ProcessActiveTiles(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.ExcitedGroups; return; case ProcessState.ExcitedGroups: - ProcessExcitedGroups(); + if (!ProcessExcitedGroups(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.HighPressureDelta; return; case ProcessState.HighPressureDelta: - ProcessHighPressureDelta(); + if (!ProcessHighPressureDelta(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.Hotspots; break; case ProcessState.Hotspots: - ProcessHotspots(); + if (!ProcessHotspots(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.Superconductivity; break; case ProcessState.Superconductivity: - ProcessSuperconductivity(); + if (!ProcessSuperconductivity(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.PipeNet; break; case ProcessState.PipeNet: - ProcessPipeNets(); + if (!ProcessPipeNets(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.PipeNetDevices; break; case ProcessState.PipeNetDevices: - ProcessPipeNetDevices(); + if (!ProcessPipeNetDevices(_paused)) + { + _paused = true; + return; + } + + _paused = false; _state = ProcessState.TileEqualize; break; } @@ -458,47 +548,71 @@ namespace Content.Server.GameObjects.Components.Atmos UpdateCounter++; } - public void ProcessTileEqualize() + public bool ProcessTileEqualize(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunTiles = new Queue(_activeTiles); + var number = 0; - foreach (var tile in _activeTiles.ToArray()) + while (_currentRunTiles.Count > 0) { + var tile = _currentRunTiles.Dequeue(); tile.EqualizePressureInZone(UpdateCounter); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - public void ProcessActiveTiles() + public bool ProcessActiveTiles(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunTiles = new Queue(_activeTiles); + var number = 0; - foreach (var tile in _activeTiles.ToArray()) + while (_currentRunTiles.Count > 0) { + var tile = _currentRunTiles.Dequeue(); tile.ProcessCell(UpdateCounter); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - public void ProcessExcitedGroups() + public bool ProcessExcitedGroups(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunExcitedGroups = new Queue(_excitedGroups); + var number = 0; - foreach (var excitedGroup in _excitedGroups.ToArray()) + while (_currentRunExcitedGroups.Count > 0) { + var excitedGroup = _currentRunExcitedGroups.Dequeue(); excitedGroup.BreakdownCooldown++; excitedGroup.DismantleCooldown++; @@ -512,17 +626,27 @@ namespace Content.Server.GameObjects.Components.Atmos number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - public void ProcessHighPressureDelta() + public bool ProcessHighPressureDelta(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunTiles = new Queue(_highPressureDelta); + var number = 0; - foreach (var tile in _highPressureDelta.ToArray()) + while (_currentRunTiles.Count > 0) { + var tile = _currentRunTiles.Dequeue(); tile.HighPressureMovements(); tile.PressureDifference = 0f; tile.PressureSpecificTarget = null; @@ -532,83 +656,124 @@ namespace Content.Server.GameObjects.Components.Atmos number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - private void ProcessHotspots() + private bool ProcessHotspots(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunTiles = new Queue(_hotspotTiles); + var number = 0; - foreach (var hotspot in _hotspotTiles.ToArray()) + while (_currentRunTiles.Count > 0) { + var hotspot = _currentRunTiles.Dequeue(); hotspot.ProcessHotspot(); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - private void ProcessSuperconductivity() + private bool ProcessSuperconductivity(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunTiles = new Queue(_superconductivityTiles); + var number = 0; - foreach (var superconductivity in _superconductivityTiles.ToArray()) + while (_currentRunTiles.Count > 0) { + var superconductivity = _currentRunTiles.Dequeue(); superconductivity.Superconduct(); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } + + _superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - private void ProcessPipeNets() + private bool ProcessPipeNets(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunPipeNet = new Queue(_pipeNets); + var number = 0; - var pipeNets = _pipeNets.ToArray(); - var netCount = pipeNets.Count(); - for ( ; _pipeNetIndex < netCount; _pipeNetIndex++) + while (_currentRunPipeNet.Count > 0) { - pipeNets[_pipeNetIndex].Update(); + var pipenet = _currentRunPipeNet.Dequeue(); + pipenet.Update(); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } - _pipeNetIndex = 0; + + _pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } - private void ProcessPipeNetDevices() + private bool ProcessPipeNetDevices(bool resumed = false) { _stopwatch.Restart(); + if(!resumed) + _currentRunPipeNetDevice = new Queue(_pipeNetDevices); + var number = 0; - var pipeNetDevices = _pipeNetDevices.ToArray(); - var deviceCount = pipeNetDevices.Count(); - for ( ; _deviceIndex < deviceCount; _deviceIndex++) + while (_currentRunPipeNet.Count > 0) { - pipeNetDevices[_deviceIndex].Update(); + var device = _currentRunPipeNetDevice.Dequeue(); + device.Update(); if (number++ < LagCheckIterations) continue; number = 0; // Process the rest next time. if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) - return; + { + _pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return false; + } } - _deviceIndex = 0; + + _pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; + return true; } + private AirtightComponent? GetObstructingComponent(MapIndices indices) { if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default; @@ -622,12 +787,6 @@ namespace Content.Server.GameObjects.Components.Atmos return null; } - private static readonly Direction[] Cardinal = - new [] - { - Direction.North, Direction.East, Direction.South, Direction.West - }; - public void Dispose() { diff --git a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs index 9037243434..84bce7a646 100644 --- a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs @@ -1,25 +1,50 @@ #nullable enable +using System; +using System.Collections.Generic; +using System.Linq; using Content.Server.Atmos; +using Content.Server.Atmos.Reactions; +using Content.Server.Interfaces; using Content.Shared.GameObjects.EntitySystems.Atmos; using JetBrains.Annotations; using Robust.Server.Interfaces.Timing; +using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.Map; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; using Robust.Shared.Map; +using Robust.Shared.Prototypes; namespace Content.Server.GameObjects.EntitySystems { [UsedImplicitly] public class AtmosphereSystem : SharedAtmosphereSystem { + [Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPauseManager _pauseManager = default!; + [Dependency] private IEntityManager _entityManager = default!; + + private GasReactionPrototype[] _gasReactions = Array.Empty(); + + /// + /// List of gas reactions ordered by priority. + /// + public IEnumerable GasReactions => _gasReactions!; + + /// + /// EventBus reference for gas reactions. + /// + public IEventBus EventBus => _entityManager.EventBus; public override void Initialize() { base.Initialize(); + _gasReactions = _protoMan.EnumeratePrototypes().ToArray(); + Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority)); + _mapManager.TileChanged += OnTileChanged; } diff --git a/Content.Server/Interfaces/IGasReactionEffect.cs b/Content.Server/Interfaces/IGasReactionEffect.cs index 95148d570c..7d2b991236 100644 --- a/Content.Server/Interfaces/IGasReactionEffect.cs +++ b/Content.Server/Interfaces/IGasReactionEffect.cs @@ -1,12 +1,13 @@ #nullable enable using Content.Server.Atmos; using Content.Server.Atmos.Reactions; +using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.Serialization; namespace Content.Server.Interfaces { public interface IGasReactionEffect : IExposeData { - ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder); + ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus); } } diff --git a/Content.Shared/Atmos/AtmosDirection.cs b/Content.Shared/Atmos/AtmosDirection.cs new file mode 100644 index 0000000000..450a92e4df --- /dev/null +++ b/Content.Shared/Atmos/AtmosDirection.cs @@ -0,0 +1,89 @@ +using System; +using Robust.Shared.Maths; + +namespace Content.Shared.Atmos +{ + /// + /// The reason we use this over is that we are going to do some heavy bitflag usage. + /// + [Flags] + public enum AtmosDirection : byte + { + Invalid = 0, + North = 1 << 0, + South = 1 << 1, + East = 1 << 2, + West = 1 << 3, + + NorthEast = North | East, + NorthWest = North | West, + SouthEast = South | East, + SouthWest = South | West, + + All = North | South | East | West, + } + + public static class AtmosDirectionHelpers + { + public static AtmosDirection GetOpposite(this AtmosDirection direction) + { + return direction switch + { + AtmosDirection.North => AtmosDirection.South, + AtmosDirection.South => AtmosDirection.North, + AtmosDirection.East => AtmosDirection.West, + AtmosDirection.West => AtmosDirection.East, + AtmosDirection.NorthEast => AtmosDirection.SouthWest, + AtmosDirection.NorthWest => AtmosDirection.SouthEast, + AtmosDirection.SouthEast => AtmosDirection.NorthWest, + AtmosDirection.SouthWest => AtmosDirection.NorthEast, + _ => throw new ArgumentOutOfRangeException(nameof(direction)) + }; + } + + public static Direction ToDirection(this AtmosDirection direction) + { + return direction switch + { + AtmosDirection.North => Direction.North, + AtmosDirection.South => Direction.South, + AtmosDirection.East => Direction.East, + AtmosDirection.West => Direction.West, + AtmosDirection.NorthEast => Direction.NorthEast, + AtmosDirection.NorthWest => Direction.NorthWest, + AtmosDirection.SouthEast => Direction.SouthEast, + AtmosDirection.SouthWest => Direction.SouthWest, + AtmosDirection.Invalid => Direction.Invalid, + _ => throw new ArgumentOutOfRangeException(nameof(direction)) + }; + } + + public static AtmosDirection ToAtmosDirection(this Direction direction) + { + return direction switch + { + Direction.North => AtmosDirection.North, + Direction.South => AtmosDirection.South, + Direction.East => AtmosDirection.East, + Direction.West => AtmosDirection.West, + Direction.NorthEast => AtmosDirection.NorthEast, + Direction.NorthWest => AtmosDirection.NorthWest, + Direction.SouthEast => AtmosDirection.SouthEast, + Direction.SouthWest => AtmosDirection.SouthWest, + Direction.Invalid => AtmosDirection.Invalid, + _ => throw new ArgumentOutOfRangeException(nameof(direction)) + }; + } + + public static int ToIndex(this AtmosDirection direction) + { + // This will throw if you pass an invalid direction. Not this method's fault, but yours! + return (int) Math.Log2((int) direction); + } + + public static AtmosDirection WithFlag(this AtmosDirection direction, AtmosDirection other) + { + return direction | other; + } + } +} diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index dbd6bf073f..f9391a040c 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -211,6 +211,12 @@ public const int LowPressureDamage = 4; public const float WindowHeatTransferCoefficient = 0.1f; + + /// + /// Directions that atmos currently supports. Modify in case of multi-z. + /// See on the server. + /// + public const int Directions = 4; } /// diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 9a4d47bb02..5bf9f9cdd9 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -84,6 +84,7 @@ True True True + True True True True @@ -96,6 +97,7 @@ True True True + True True True True