Atmos optimizations (#1944)

* Adds IFireAct, ITemperatureExpose

* Use AtmosDirection instead of Direction for Atmos

* Refactor atmos to heavily rely on arrays and bitflags.
Adds F A S T M O S and reduces atmos tech debt heavily.

* Optimize and fix more stuff

* Kinda improve superconduction

* Pipenet is a word

* T U R B O M O S

* Address reviews

* Small optimization

* Superconduct is also a word

* Remove check

* Cleanup tile atmosphere

* Correct a comment
This commit is contained in:
Víctor Aguilera Puerto
2020-08-28 14:32:56 +02:00
committed by GitHub
parent 3758eb1b60
commit fb0ac3d70e
16 changed files with 619 additions and 275 deletions

View File

@@ -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;
}
}
}

View File

@@ -129,17 +129,21 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public float Volume { get; set; } public float Volume { get; set; }
public GasMixture() public GasMixture() : this(null)
{ {
_atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
} }
public GasMixture(float volume) public GasMixture(AtmosphereSystem? atmosphereSystem)
{
_atmosphereSystem = atmosphereSystem ?? EntitySystem.Get<AtmosphereSystem>();
}
public GasMixture(float volume, AtmosphereSystem? atmosphereSystem = null)
{ {
if (volume < 0) if (volume < 0)
volume = 0; volume = 0;
Volume = volume; Volume = volume;
_atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get<AtmosphereSystem>();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -224,12 +228,12 @@ namespace Content.Server.Atmos
public GasMixture RemoveRatio(float ratio) public GasMixture RemoveRatio(float ratio)
{ {
if(ratio <= 0) if(ratio <= 0)
return new GasMixture(Volume); return new GasMixture(Volume, _atmosphereSystem);
if (ratio > 1) if (ratio > 1)
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++) for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{ {
@@ -252,7 +256,7 @@ namespace Content.Server.Atmos
public void CopyFromMutable(GasMixture sample) public void CopyFromMutable(GasMixture sample)
{ {
if (Immutable) return; if (Immutable) return;
sample._moles.AsSpan().CopyTo(_moles.AsSpan()); sample._moles.CopyTo(_moles, 0);
Temperature = sample.Temperature; Temperature = sample.Temperature;
} }
@@ -485,8 +489,7 @@ namespace Content.Server.Atmos
var temperature = Temperature; var temperature = Temperature;
var energy = ThermalEnergy; var energy = ThermalEnergy;
// TODO ATMOS Take reaction priority into account! foreach (var prototype in _atmosphereSystem.GasReactions)
foreach (var prototype in IoCManager.Resolve<IPrototypeManager>().EnumeratePrototypes<GasReactionPrototype>())
{ {
if (energy < prototype.MinimumEnergyRequirement || if (energy < prototype.MinimumEnergyRequirement ||
temperature < prototype.MinimumTemperatureRequirement) temperature < prototype.MinimumTemperatureRequirement)
@@ -508,7 +511,7 @@ namespace Content.Server.Atmos
if (!doReaction) if (!doReaction)
continue; continue;
reaction = prototype.React(this, holder); reaction = prototype.React(this, holder, _atmosphereSystem.EventBus);
if(reaction.HasFlag(ReactionResult.StopReactions)) if(reaction.HasFlag(ReactionResult.StopReactions))
break; break;
} }
@@ -588,7 +591,7 @@ namespace Content.Server.Atmos
public object Clone() public object Clone()
{ {
var newMixture = new GasMixture() var newMixture = new GasMixture(_atmosphereSystem)
{ {
_moles = (float[])_moles.Clone(), _moles = (float[])_moles.Clone(),
_molesArchived = (float[])_molesArchived.Clone(), _molesArchived = (float[])_molesArchived.Clone(),

View File

@@ -1,6 +1,7 @@
#nullable enable #nullable enable
using System; using System;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.GameObjects.Components.Atmos;
using Content.Shared.Atmos;
using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.Physics; using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
@@ -24,7 +25,7 @@ namespace Content.Server.Atmos
private const float ProbabilityBasePercent = 10f; private const float ProbabilityBasePercent = 10f;
private const float ThrowForce = 100f; 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) float pressureResistanceProbDelta, GridCoordinates throwTarget)
{ {
if (ControlledComponent == null) if (ControlledComponent == null)
@@ -54,14 +55,14 @@ namespace Content.Server.Atmos
if (throwTarget != GridCoordinates.InvalidGrid) if (throwTarget != GridCoordinates.InvalidGrid)
{ {
var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f; 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; LinearVelocity = pos * moveForce;
} }
else else
{ {
var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f); var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f);
LinearVelocity = direction.ToVec() * moveForce; LinearVelocity = direction.ToDirection().ToVec() * moveForce;
} }
pressureComponent.LastHighPressureMovementAirCycle = cycle; pressureComponent.LastHighPressureMovementAirCycle = cycle;

View File

@@ -13,11 +13,6 @@ namespace Content.Server.Atmos
/// </summary> /// </summary>
int UpdateCounter { get; } int UpdateCounter { get; }
/// <summary>
/// How many tiles have high pressure delta.
/// </summary>
int HighPressureDeltaCount { get; }
/// <summary> /// <summary>
/// Control variable for equalization. /// Control variable for equalization.
/// </summary> /// </summary>
@@ -115,14 +110,14 @@ namespace Content.Server.Atmos
/// </summary> /// </summary>
/// <param name="indices"></param> /// <param name="indices"></param>
/// <returns></returns> /// <returns></returns>
TileAtmosphere GetTile(MapIndices indices); TileAtmosphere GetTile(MapIndices indices, bool createSpace = true);
/// <summary> /// <summary>
/// Returns a tile. /// Returns a tile.
/// </summary> /// </summary>
/// <param name="coordinates"></param> /// <param name="coordinates"></param>
/// <returns></returns> /// <returns></returns>
TileAtmosphere GetTile(GridCoordinates coordinates); TileAtmosphere GetTile(GridCoordinates coordinates, bool createSpace = true);
/// <summary> /// <summary>
/// Returns if the tile in question is air-blocked. /// Returns if the tile in question is air-blocked.

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using YamlDotNet.RepresentationModel; using YamlDotNet.RepresentationModel;
@@ -64,13 +65,13 @@ namespace Content.Server.Atmos.Reactions
serializer.DataField(ref _effects, "effects", new List<IGasReactionEffect>()); serializer.DataField(ref _effects, "effects", new List<IGasReactionEffect>());
} }
public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder) public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder, IEventBus eventBus)
{ {
var result = ReactionResult.NoReaction; var result = ReactionResult.NoReaction;
foreach (var effect in _effects) foreach (var effect in _effects)
{ {
result |= effect.React(mixture, holder); result |= effect.React(mixture, holder, eventBus);
} }
return result; return result;

View File

@@ -1,8 +1,12 @@
#nullable enable #nullable enable
using System; using System;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Maps;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Server.Atmos.Reactions namespace Content.Server.Atmos.Reactions
@@ -10,7 +14,7 @@ namespace Content.Server.Atmos.Reactions
[UsedImplicitly] [UsedImplicitly]
public class PhoronFireReaction : IGasReactionEffect public class PhoronFireReaction : IGasReactionEffect
{ {
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder) public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus)
{ {
var energyReleased = 0f; var energyReleased = 0f;
var oldHeatCapacity = mixture.HeatCapacity; var oldHeatCapacity = mixture.HeatCapacity;
@@ -71,9 +75,7 @@ namespace Content.Server.Atmos.Reactions
{ {
location.HotspotExpose(temperature, mixture.Volume); location.HotspotExpose(temperature, mixture.Volume);
// TODO ATMOS Expose temperature all items on cell eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume));
location.TemperatureExpose(mixture, temperature, mixture.Volume);
} }
} }

View File

@@ -1,7 +1,10 @@
#nullable enable #nullable enable
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Maps;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Server.Atmos.Reactions 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 energyReleased = 0f;
var oldHeatCapacity = mixture.HeatCapacity; var oldHeatCapacity = mixture.HeatCapacity;
@@ -66,9 +69,7 @@ namespace Content.Server.Atmos.Reactions
{ {
location.HotspotExpose(temperature, mixture.Volume); location.HotspotExpose(temperature, mixture.Volume);
// TODO ATMOS Expose temperature all items on cell eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume));
location.TemperatureExpose(mixture, temperature, mixture.Volume);
} }
} }

View File

@@ -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;
}
}
}

View File

@@ -1,4 +1,5 @@
using System; using System;
using Content.Shared.Atmos;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -30,15 +31,15 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public float TransferDirectionSouth; public float TransferDirectionSouth;
public float this[Direction direction] public float this[AtmosDirection direction]
{ {
get => get =>
direction switch direction switch
{ {
Direction.East => TransferDirectionEast, AtmosDirection.East => TransferDirectionEast,
Direction.West => TransferDirectionWest, AtmosDirection.West => TransferDirectionWest,
Direction.North => TransferDirectionNorth, AtmosDirection.North => TransferDirectionNorth,
Direction.South => TransferDirectionSouth, AtmosDirection.South => TransferDirectionSouth,
_ => throw new ArgumentOutOfRangeException(nameof(direction)) _ => throw new ArgumentOutOfRangeException(nameof(direction))
}; };
@@ -46,16 +47,16 @@ namespace Content.Server.Atmos
{ {
switch (direction) switch (direction)
{ {
case Direction.East: case AtmosDirection.East:
TransferDirectionEast = value; TransferDirectionEast = value;
break; break;
case Direction.West: case AtmosDirection.West:
TransferDirectionWest = value; TransferDirectionWest = value;
break; break;
case Direction.North: case AtmosDirection.North:
TransferDirectionNorth = value; TransferDirectionNorth = value;
break; break;
case Direction.South: case AtmosDirection.South:
TransferDirectionSouth = value; TransferDirectionSouth = value;
break; break;
default: 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] [ViewVariables]
public float CurrentTransferAmount; public float CurrentTransferAmount;
public Direction CurrentTransferDirection; public AtmosDirection CurrentTransferDirection;
[ViewVariables] [ViewVariables]
public bool FastDone; public bool FastDone;

View File

@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.Atmos.Reactions; using Content.Server.Atmos.Reactions;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.GameObjects.Components.Atmos;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.EntitySystems.Atmos; using Content.Server.GameObjects.EntitySystems.Atmos;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Shared.Atmos; using Content.Shared.Atmos;
@@ -12,13 +11,13 @@ using Content.Shared.Audio;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -28,18 +27,15 @@ namespace Content.Server.Atmos
{ {
public class TileAtmosphere : IGasMixtureHolder public class TileAtmosphere : IGasMixtureHolder
{ {
[Robust.Shared.IoC.Dependency] private IRobustRandom _robustRandom = default!; [Robust.Shared.IoC.Dependency] private readonly IRobustRandom _robustRandom = default!;
[Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!; [Robust.Shared.IoC.Dependency] private readonly IEntityManager _entityManager = default!;
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = 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] [ViewVariables] private int _archivedCycle;
private int _archivedCycle = 0; [ViewVariables] private int _currentCycle;
[ViewVariables]
private int _currentCycle = 0;
[ViewVariables] [ViewVariables]
private static GasTileOverlaySystem _gasTileOverlaySystem; private static GasTileOverlaySystem _gasTileOverlaySystem;
@@ -51,13 +47,13 @@ namespace Content.Server.Atmos
private float _temperatureArchived = Atmospherics.T20C; 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. // 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] [ViewVariables]
public TileAtmosphere PressureSpecificTarget { get; set; } = null; public TileAtmosphere PressureSpecificTarget { get; set; }
[ViewVariables] [ViewVariables]
public float PressureDifference { get; set; } = 0; public float PressureDifference { get; set; }
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float HeatCapacity { get; set; } = 1f; public float HeatCapacity { get; set; } = 1f;
@@ -66,13 +62,19 @@ namespace Content.Server.Atmos
public float ThermalConductivity => Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.05f; public float ThermalConductivity => Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.05f;
[ViewVariables] [ViewVariables]
public bool Excited { get; set; } = false; public bool Excited { get; set; }
[ViewVariables] [ViewVariables]
private GridAtmosphereComponent _gridAtmosphereComponent; private readonly GridAtmosphereComponent _gridAtmosphereComponent;
/// <summary>
/// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW)
/// </summary>
[ViewVariables]
private readonly TileAtmosphere[] _adjacentTiles = new TileAtmosphere[Atmospherics.Directions];
[ViewVariables] [ViewVariables]
private readonly Dictionary<Direction, TileAtmosphere> _adjacentTiles = new Dictionary<Direction, TileAtmosphere>(); private AtmosDirection _adjacentBits = AtmosDirection.Invalid;
[ViewVariables] [ViewVariables]
private TileAtmosInfo _tileAtmosInfo; private TileAtmosInfo _tileAtmosInfo;
@@ -80,7 +82,7 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public Hotspot Hotspot; public Hotspot Hotspot;
private Direction _pressureDirection; private AtmosDirection _pressureDirection;
[ViewVariables] [ViewVariables]
public GridId GridIndex { get; } public GridId GridIndex { get; }
@@ -100,14 +102,16 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public bool BlocksAir => _gridAtmosphereComponent.IsAirBlocked(GridIndices); 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); IoCManager.InjectDependencies(this);
_gridAtmosphereComponent = atmosphereComponent; _gridAtmosphereComponent = atmosphereComponent;
GridIndex = gridIndex; GridIndex = gridIndex;
GridIndices = gridIndices; GridIndices = gridIndices;
Air = mixture; Air = mixture;
ResetTileAtmosInfo();
if(immutable)
Air?.MarkImmutable();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -163,7 +167,7 @@ namespace Content.Server.Atmos
} }
} }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void HighPressureMovements() public void HighPressureMovements()
{ {
// TODO ATMOS finish this // 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))); 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))) foreach (var entity in _entityManager.GetEntitiesIntersecting(_mapManager.GetGrid(GridIndex).ParentMapId, Box2.UnitCentered.Translated(GridIndices)))
{ {
if (!entity.TryGetComponent(out ICollidableComponent physics) if (!entity.TryGetComponent(out ICollidableComponent physics)
@@ -195,7 +198,7 @@ namespace Content.Server.Atmos
if (PressureDifference > 100) if (PressureDifference > 100)
{ {
// Do space wind graphics here! // TODO ATMOS Do space wind graphics here!
} }
_soundCooldown++; _soundCooldown++;
@@ -220,20 +223,22 @@ namespace Content.Server.Atmos
} }
} }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EqualizePressureInZone(int cycleNum) public void EqualizePressureInZone(int cycleNum)
{ {
if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done. if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done.
ResetTileAtmosInfo(); _tileAtmosInfo = new TileAtmosInfo();
var startingMoles = Air.TotalMoles; var startingMoles = Air.TotalMoles;
var runAtmos = false; var runAtmos = false;
// We need to figure if this is necessary // 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; if (other?.Air == null) continue;
var comparisonMoles = other.Air.TotalMoles; var comparisonMoles = other.Air.TotalMoles;
if (!(MathF.Abs(comparisonMoles - startingMoles) > Atmospherics.MinimumMolesDeltaToMove)) continue; if (!(MathF.Abs(comparisonMoles - startingMoles) > Atmospherics.MinimumMolesDeltaToMove)) continue;
@@ -265,13 +270,15 @@ namespace Content.Server.Atmos
totalMoles += tileMoles; 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?.Air == null) continue;
if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue; if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
adj.ResetTileAtmosInfo(); adj._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
adj._tileAtmosInfo.LastQueueCycle = queueCycle;
if(tileCount < Atmospherics.ZumosHardTileLimit) if(tileCount < Atmospherics.ZumosHardTileLimit)
tiles[tileCount++] = adj; tiles[tileCount++] = adj;
if (adj.Air.Immutable) if (adj.Air.Immutable)
@@ -326,47 +333,42 @@ namespace Content.Server.Atmos
if (giverTilesLength > logN && takerTilesLength > logN) if (giverTilesLength > logN && takerTilesLength > logN)
{ {
// Even if it fails, it will speed up the next part. // 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++) for (var i = 0; i < tileCount; i++)
{ {
var tile = tiles[i]; var tile = tiles[i];
tile._tileAtmosInfo.FastDone = true; tile._tileAtmosInfo.FastDone = true;
if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue; if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
var eligibleDirections = ArrayPool<Direction>.Shared.Rent(4); var eligibleDirections = AtmosDirection.Invalid;
var eligibleDirectionCount = 0; 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. // skip anything that isn't part of our current processing block.
if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle) if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
continue; continue;
eligibleDirections[eligibleDirectionCount++] = direction; eligibleDirections |= direction;
eligibleDirectionCount++;
} }
if (eligibleDirectionCount <= 0) if (eligibleDirectionCount <= 0)
continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this. continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
var molesToMove = tile._tileAtmosInfo.MoleDelta / eligibleDirectionCount; var molesToMove = tile._tileAtmosInfo.MoleDelta / eligibleDirectionCount;
foreach (var direction in Cardinal) for (var j = 0; j < Atmospherics.Directions; j++)
{ {
var hasDirection = false; var direction = (AtmosDirection) (1 << j);
for (var j = 0; j < eligibleDirectionCount; j++) if (!eligibleDirections.HasFlag(direction)) continue;
{
if (eligibleDirections[j] != direction) continue;
hasDirection = true;
break;
}
if (hasDirection || !tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
tile.AdjustEqMovement(direction, molesToMove); tile.AdjustEqMovement(direction, molesToMove);
tile._tileAtmosInfo.MoleDelta -= molesToMove; tile._tileAtmosInfo.MoleDelta -= molesToMove;
tile2._tileAtmosInfo.MoleDelta += molesToMove; tile._adjacentTiles[j]._tileAtmosInfo.MoleDelta += molesToMove;
} }
ArrayPool<Direction>.Shared.Return(eligibleDirections);
} }
giverTilesLength = 0; giverTilesLength = 0;
@@ -393,7 +395,7 @@ namespace Content.Server.Atmos
for (var j = 0; j < giverTilesLength; j++) for (var j = 0; j < giverTilesLength; j++)
{ {
var giver = giverTiles[j]; var giver = giverTiles[j];
giver._tileAtmosInfo.CurrentTransferDirection = (Direction) (-1); giver._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
giver._tileAtmosInfo.CurrentTransferAmount = 0; giver._tileAtmosInfo.CurrentTransferAmount = 0;
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl; var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
var queueLength = 0; 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. break; // We're done here now. Let's not do more work than needed.
var tile = queue[i]; 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 (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.LastQueueCycle != queueCycle) continue;
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
@@ -441,15 +445,11 @@ namespace Content.Server.Atmos
for (var i = queueLength - 1; i >= 0; i--) for (var i = queueLength - 1; i >= 0; i--)
{ {
var tile = queue[i]; var tile = queue[i];
if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && tile._tileAtmosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
tile._tileAtmosInfo.CurrentTransferDirection != (Direction) (-1))
{ {
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
tile._tileAtmosInfo.CurrentTransferAmount); tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
out var adjacent))
adjacent._tileAtmosInfo.CurrentTransferAmount +=
tile._tileAtmosInfo.CurrentTransferAmount;
tile._tileAtmosInfo.CurrentTransferAmount = 0; tile._tileAtmosInfo.CurrentTransferAmount = 0;
} }
} }
@@ -463,7 +463,7 @@ namespace Content.Server.Atmos
for (var j = 0; j < takerTilesLength; j++) for (var j = 0; j < takerTilesLength; j++)
{ {
var taker = takerTiles[j]; var taker = takerTiles[j];
taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; taker._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
taker._tileAtmosInfo.CurrentTransferAmount = 0; taker._tileAtmosInfo.CurrentTransferAmount = 0;
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl; var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
var queueLength = 0; 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. break; // We're done here now. Let's not do more work than needed.
var tile = queue[i]; var tile = queue[i];
foreach (var direction in Cardinal) for (var k = 0; k < Atmospherics.Directions; k++)
{ {
if (!tile._adjacentTiles.ContainsKey(direction)) continue; var direction = (AtmosDirection) (1 << k);
var tile2 = tile._adjacentTiles[direction]; 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 (taker._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.LastQueueCycle != queueCycle) continue;
@@ -512,18 +513,16 @@ namespace Content.Server.Atmos
for (var i = queueLength - 1; i >= 0; i--) for (var i = queueLength - 1; i >= 0; i--)
{ {
var tile = queue[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; continue;
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount); tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent)) tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
{ ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
tile._tileAtmosInfo.CurrentTransferAmount = 0; tile._tileAtmosInfo.CurrentTransferAmount = 0;
} }
} }
}
ArrayPool<TileAtmosphere>.Shared.Return(queue); ArrayPool<TileAtmosphere>.Shared.Return(queue);
} }
@@ -537,9 +536,11 @@ namespace Content.Server.Atmos
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var tile = tiles[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; if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange) continue;
_gridAtmosphereComponent.AddActiveTile(tile2); _gridAtmosphereComponent.AddActiveTile(tile2);
break; break;
@@ -555,69 +556,68 @@ namespace Content.Server.Atmos
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FinalizeEq() private void FinalizeEq()
{ {
var transferDirections = new Dictionary<Direction, float>(); var transferDirections = new float[Atmospherics.Directions];
var hasTransferDirs = false; 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; if (amount == 0) continue;
transferDirections[direction] = amount; transferDirections[i] = amount;
_tileAtmosInfo[direction] = 0; _tileAtmosInfo[i] = 0; // Set them to 0 to prevent infinite recursion.
hasTransferDirs = true; hasTransferDirs = true;
} }
if (!hasTransferDirs) return; 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 (amount > 0)
{ {
if (Air.TotalMoles < amount) if (Air.TotalMoles < amount)
FinalizeEqNeighbors(transferDirections.Keys); FinalizeEqNeighbors(transferDirections);
tile._tileAtmosInfo[direction.GetOpposite()] = 0;
tile.Air.Merge(Air.Remove(amount)); tile.Air.Merge(Air.Remove(amount));
UpdateVisuals(); UpdateVisuals();
tile.UpdateVisuals(); tile.UpdateVisuals();
ConsiderPressureDifference(direction, amount); ConsiderPressureDifference(tile, amount);
} }
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FinalizeEqNeighbors(IEnumerable<Direction> directions) private void FinalizeEqNeighbors(in float[] transferDirs)
{ {
foreach (var direction in directions) for (var i = 0; i < Atmospherics.Directions; i++)
{ {
var amount = _tileAtmosInfo[direction]; var direction = (AtmosDirection) (1 << i);
if(amount < 0 && _adjacentTiles.TryGetValue(direction, out var adjacent)) var amount = transferDirs[i];
adjacent.FinalizeEq(); if(amount < 0 && _adjacentBits.HasFlag(direction))
_adjacentTiles[i].FinalizeEq(); // A bit of recursion if needed.
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConsiderPressureDifference(Direction direction, float difference) private void ConsiderPressureDifference(TileAtmosphere other, float difference)
{ {
_gridAtmosphereComponent.AddHighPressureDelta(this); _gridAtmosphereComponent.AddHighPressureDelta(this);
if (difference > PressureDifference) if (difference > PressureDifference)
{ {
PressureDifference = difference; PressureDifference = difference;
_pressureDirection = difference < 0 ? direction.GetOpposite() : direction; _pressureDirection = ((Vector2i)(GridIndices - other.GridIndices)).GetDir().ToAtmosDirection();
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AdjustEqMovement(Direction direction, float molesToMove) private void AdjustEqMovement(AtmosDirection direction, float amount)
{ {
_tileAtmosInfo[direction] += molesToMove; _tileAtmosInfo[direction] += amount;
if(direction != Direction.Invalid && _adjacentTiles.TryGetValue(direction, out var adj)) _adjacentTiles[direction.ToIndex()]._tileAtmosInfo[direction.GetOpposite()] -= amount;
adj._tileAtmosInfo[direction.GetOpposite()] -= molesToMove;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetTileAtmosInfo()
{
_tileAtmosInfo = new TileAtmosInfo {CurrentTransferDirection = Direction.Invalid};
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -635,9 +635,13 @@ namespace Content.Server.Atmos
_currentCycle = fireCount; _currentCycle = fireCount;
var adjacentTileLength = 0; 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; if(enemyTile?.Air == null) continue;
adjacentTileLength++; adjacentTileLength++;
if (fireCount <= enemyTile._currentCycle) continue; if (fireCount <= enemyTile._currentCycle) continue;
@@ -685,11 +689,11 @@ namespace Content.Server.Atmos
// Space wind! // Space wind!
if (difference > 0) if (difference > 0)
{ {
ConsiderPressureDifference(direction, difference); ConsiderPressureDifference(enemyTile, difference);
} }
else else
{ {
enemyTile.ConsiderPressureDifference(direction.GetOpposite(), -difference); enemyTile.ConsiderPressureDifference(this, -difference);
} }
LastShareCheck(); LastShareCheck();
@@ -737,7 +741,7 @@ namespace Content.Server.Atmos
if (Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread) if (Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread)
{ {
var radiatedTemperature = Air.Temperature * Atmospherics.FireSpreadRadiosityScale; var radiatedTemperature = Air.Temperature * Atmospherics.FireSpreadRadiosityScale;
foreach (var (_, tile) in _adjacentTiles) foreach (var tile in _adjacentTiles)
{ {
if(!tile.Hotspot.Valid) if(!tile.Hotspot.Valid)
tile.HotspotExpose(radiatedTemperature, Atmospherics.CellVolume/4); tile.HotspotExpose(radiatedTemperature, Atmospherics.CellVolume/4);
@@ -771,17 +775,19 @@ namespace Content.Server.Atmos
else else
{ {
var affected = Air.RemoveRatio(Hotspot.Volume / Air.Volume); var affected = Air.RemoveRatio(Hotspot.Volume / Air.Volume);
if (affected != null)
{
affected.Temperature = Hotspot.Temperature; affected.Temperature = Hotspot.Temperature;
affected.React(this); affected.React(this);
Hotspot.Temperature = affected.Temperature; Hotspot.Temperature = affected.Temperature;
Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate; Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
AssumeAir(affected); 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() private bool ConsiderSuperconductivity()
@@ -806,15 +812,16 @@ namespace Content.Server.Atmos
public void Superconduct() public void Superconduct()
{ {
var directions = ConductivityDirections(); 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) var direction = (AtmosDirection) (1 << i);
{ if (!directions.HasFlag(direction)) continue;
if (!adjacentTiles.TryGetValue(direction, out var adjacent)) continue;
if (adjacent.ThermalConductivity == 0f) var adjacent = _adjacentTiles[direction.ToIndex()];
// TODO ATMOS handle adjacent being null.
if (adjacent == null || adjacent.ThermalConductivity == 0f)
continue; continue;
if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter) if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter)
@@ -824,7 +831,6 @@ namespace Content.Server.Atmos
adjacent.ConsiderSuperconductivity(); adjacent.ConsiderSuperconductivity();
} }
}
RadiateToSpace(); RadiateToSpace();
@@ -917,20 +923,20 @@ namespace Content.Server.Atmos
} }
} }
public Direction[] ConductivityDirections() public AtmosDirection ConductivityDirections()
{ {
if(BlocksAir) if(BlocksAir)
{ {
if(_archivedCycle < _gridAtmosphereComponent.UpdateCounter) if(_archivedCycle < _gridAtmosphereComponent.UpdateCounter)
Archive(_gridAtmosphereComponent.UpdateCounter); Archive(_gridAtmosphereComponent.UpdateCounter);
return Cardinal; return AtmosDirection.All;
} }
// TODO ATMOS check if this is correct // TODO ATMOS check if this is correct
return Cardinal; return AtmosDirection.All;
} }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExplosivelyDepressurize(int cycleNum) public void ExplosivelyDepressurize(int cycleNum)
{ {
if (Air == null) return; if (Air == null) return;
@@ -947,14 +953,13 @@ namespace Content.Server.Atmos
tiles[tileCount++] = this; tiles[tileCount++] = this;
ResetTileAtmosInfo(); _tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
_tileAtmosInfo.LastQueueCycle = queueCycle;
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var tile = tiles[i]; var tile = tiles[i];
tile._tileAtmosInfo.LastCycle = cycleNum; tile._tileAtmosInfo.LastCycle = cycleNum;
tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
if (tile.Air.Immutable) if (tile.Air.Immutable)
{ {
spaceTiles[spaceTileCount++] = tile; spaceTiles[spaceTileCount++] = tile;
@@ -962,18 +967,19 @@ namespace Content.Server.Atmos
} }
else 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.Air == null) continue;
if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue; if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
tile.ConsiderFirelocks(tile2); tile.ConsiderFirelocks(tile2);
// The firelocks might have closed on us. // The firelocks might have closed on us.
if (tile._adjacentTiles[direction]?.Air == null) continue; if (!tile._adjacentBits.HasFlag(direction)) continue;
tile2.ResetTileAtmosInfo(); tile2._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
tile2._tileAtmosInfo.LastQueueCycle = queueCycle;
tiles[tileCount++] = tile2; tiles[tileCount++] = tile2;
} }
} }
@@ -991,18 +997,21 @@ namespace Content.Server.Atmos
var tile = spaceTiles[i]; var tile = spaceTiles[i];
progressionOrder[progressionCount++] = tile; progressionOrder[progressionCount++] = tile;
tile._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow; tile._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid; tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
} }
for (var i = 0; i < progressionCount; i++) for (var i = 0; i < progressionCount; i++)
{ {
var tile = progressionOrder[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.LastQueueCycle != queueCycle) continue;
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) 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.CurrentTransferDirection = direction.GetOpposite();
tile2._tileAtmosInfo.CurrentTransferAmount = 0; tile2._tileAtmosInfo.CurrentTransferAmount = 0;
tile2.PressureSpecificTarget = tile.PressureSpecificTarget; tile2.PressureSpecificTarget = tile.PressureSpecificTarget;
@@ -1014,10 +1023,11 @@ namespace Content.Server.Atmos
for (var i = progressionCount - 1; i >= 0; i--) for (var i = progressionCount - 1; i >= 0; i--)
{ {
var tile = progressionOrder[i]; var tile = progressionOrder[i];
if (tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue; if (tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
_gridAtmosphereComponent.AddHighPressureDelta(tile); _gridAtmosphereComponent.AddHighPressureDelta(tile);
_gridAtmosphereComponent.AddActiveTile(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; var sum = tile2.Air.TotalMoles;
totalGasesRemoved += sum; totalGasesRemoved += sum;
tile._tileAtmosInfo.CurrentTransferAmount += sum; tile._tileAtmosInfo.CurrentTransferAmount += sum;
@@ -1025,7 +1035,7 @@ namespace Content.Server.Atmos
tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount; tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount;
tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection; tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
if (tile2._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) if (tile2._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
{ {
tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount; tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount;
tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection; tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
@@ -1082,21 +1092,30 @@ namespace Content.Server.Atmos
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UpdateAdjacent() 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)); _adjacentBits |= direction;
_adjacentTiles[direction] = adjacent;
adjacent.UpdateAdjacent(direction.GetOpposite());
} }
} }
} }
public void UpdateAdjacent(Direction direction) public void UpdateAdjacent(AtmosDirection direction)
{ {
if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction))) if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection())))
_adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction)); {
_adjacentTiles[direction.ToIndex()] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction.ToDirection()));
}
} }
private void LastShareCheck() private void LastShareCheck()
@@ -1111,13 +1130,7 @@ namespace Content.Server.Atmos
} }
} }
private static readonly Direction[] Cardinal = public void TemperatureExpose(GasMixture air, float temperature, float volume)
new Direction[]
{
Direction.North, Direction.East, Direction.South, Direction.West
};
public void TemperatureExpose(GasMixture mixture, float temperature, float cellVolume)
{ {
// TODO ATMOS do this // TODO ATMOS do this
} }

View File

@@ -36,7 +36,7 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <summary> /// <summary>
/// Check current execution time every n instances processed. /// Check current execution time every n instances processed.
/// </summary> /// </summary>
private const int LagCheckIterations = 15; private const int LagCheckIterations = 30;
/// <summary> /// <summary>
/// Max milliseconds allowed for atmos updates. /// Max milliseconds allowed for atmos updates.
@@ -50,46 +50,93 @@ namespace Content.Server.GameObjects.Components.Atmos
public override string Name => "GridAtmosphere"; public override string Name => "GridAtmosphere";
private bool _paused = false;
private float _timer = 0f; private float _timer = 0f;
private Stopwatch _stopwatch = new Stopwatch(); private Stopwatch _stopwatch = new Stopwatch();
[ViewVariables]
public int UpdateCounter { get; private set; } = 0; public int UpdateCounter { get; private set; } = 0;
[ViewVariables]
private double _tileEqualizeLastProcess;
[ViewVariables] [ViewVariables]
private readonly HashSet<ExcitedGroup> _excitedGroups = new HashSet<ExcitedGroup>(1000); private readonly HashSet<ExcitedGroup> _excitedGroups = new HashSet<ExcitedGroup>(1000);
[ViewVariables]
private int ExcitedGroupCount => _excitedGroups.Count;
[ViewVariables]
private double _excitedGroupLastProcess;
[ViewVariables] [ViewVariables]
private readonly Dictionary<MapIndices, TileAtmosphere> _tiles = new Dictionary<MapIndices, TileAtmosphere>(1000); private readonly Dictionary<MapIndices, TileAtmosphere> _tiles = new Dictionary<MapIndices, TileAtmosphere>(1000);
[ViewVariables] [ViewVariables]
private readonly HashSet<TileAtmosphere> _activeTiles = new HashSet<TileAtmosphere>(1000); private readonly HashSet<TileAtmosphere> _activeTiles = new HashSet<TileAtmosphere>(1000);
[ViewVariables]
private int ActiveTilesCount => _activeTiles.Count;
[ViewVariables]
private double _activeTilesLastProcess;
[ViewVariables] [ViewVariables]
private readonly HashSet<TileAtmosphere> _hotspotTiles = new HashSet<TileAtmosphere>(1000); private readonly HashSet<TileAtmosphere> _hotspotTiles = new HashSet<TileAtmosphere>(1000);
[ViewVariables]
private int HotspotTilesCount => _hotspotTiles.Count;
[ViewVariables]
private double _hotspotsLastProcess;
[ViewVariables] [ViewVariables]
private readonly HashSet<TileAtmosphere> _superconductivityTiles = new HashSet<TileAtmosphere>(1000); private readonly HashSet<TileAtmosphere> _superconductivityTiles = new HashSet<TileAtmosphere>(1000);
[ViewVariables]
private int SuperconductivityTilesCount => _superconductivityTiles.Count;
[ViewVariables]
private double _superconductivityLastProcess;
[ViewVariables] [ViewVariables]
private readonly HashSet<MapIndices> _invalidatedCoords = new HashSet<MapIndices>(1000); private readonly HashSet<MapIndices> _invalidatedCoords = new HashSet<MapIndices>(1000);
[ViewVariables]
private int InvalidatedCoordsCount => _invalidatedCoords.Count;
[ViewVariables] [ViewVariables]
private HashSet<TileAtmosphere> _highPressureDelta = new HashSet<TileAtmosphere>(1000); private HashSet<TileAtmosphere> _highPressureDelta = new HashSet<TileAtmosphere>(1000);
[ViewVariables] [ViewVariables]
private readonly List<IPipeNet> _pipeNets = new List<IPipeNet>(); private int HighPressureDeltaCount => _highPressureDelta.Count;
/// <summary>
/// Index of most recently updated <see cref="IPipeNet"/>.
/// </summary>
private int _pipeNetIndex = 0;
[ViewVariables] [ViewVariables]
private readonly List<PipeNetDeviceComponent> _pipeNetDevices = new List<PipeNetDeviceComponent>(); private double _highPressureDeltaLastProcess;
/// <summary> [ViewVariables]
/// Index of most recently updated <see cref="PipeNetDeviceComponent"/>. private readonly HashSet<IPipeNet> _pipeNets = new HashSet<IPipeNet>();
/// </summary>
private int _deviceIndex = 0; [ViewVariables]
private double _pipeNetLastProcess;
[ViewVariables]
private readonly HashSet<PipeNetDeviceComponent> _pipeNetDevices = new HashSet<PipeNetDeviceComponent>();
[ViewVariables]
private double _pipeNetDevicesLastProcess;
[ViewVariables]
private Queue<TileAtmosphere> _currentRunTiles = new Queue<TileAtmosphere>();
[ViewVariables]
private Queue<ExcitedGroup> _currentRunExcitedGroups = new Queue<ExcitedGroup>();
[ViewVariables]
private Queue<IPipeNet> _currentRunPipeNet = new Queue<IPipeNet>();
[ViewVariables]
private Queue<PipeNetDeviceComponent> _currentRunPipeNetDevice = new Queue<PipeNetDeviceComponent>();
[ViewVariables] [ViewVariables]
private ProcessState _state = ProcessState.TileEqualize; private ProcessState _state = ProcessState.TileEqualize;
@@ -204,9 +251,10 @@ namespace Content.Server.GameObjects.Components.Atmos
tile.UpdateAdjacent(); tile.UpdateAdjacent();
tile.UpdateVisuals(); 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); var otherTile = GetTile(otherIndices);
AddActiveTile(otherTile); AddActiveTile(otherTile);
otherTile?.UpdateAdjacent(direction.GetOpposite()); otherTile?.UpdateAdjacent(direction.GetOpposite());
@@ -324,7 +372,6 @@ namespace Content.Server.GameObjects.Components.Atmos
public void RemovePipeNet(IPipeNet pipeNet) public void RemovePipeNet(IPipeNet pipeNet)
{ {
_pipeNets.Remove(pipeNet); _pipeNets.Remove(pipeNet);
_deviceIndex = 0;
} }
public void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice) public void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice)
@@ -335,28 +382,25 @@ namespace Content.Server.GameObjects.Components.Atmos
public void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice) public void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice)
{ {
_pipeNetDevices.Remove(pipeNetDevice); _pipeNetDevices.Remove(pipeNetDevice);
_deviceIndex = 0;
} }
/// <inheritdoc /> /// <inheritdoc />
public TileAtmosphere? GetTile(GridCoordinates coordinates) public TileAtmosphere? GetTile(GridCoordinates coordinates, bool createSpace = true)
{ {
return GetTile(coordinates.ToMapIndices(_mapManager)); return GetTile(coordinates.ToMapIndices(_mapManager), createSpace);
} }
/// <inheritdoc /> /// <inheritdoc />
public TileAtmosphere? GetTile(MapIndices indices) public TileAtmosphere? GetTile(MapIndices indices, bool createSpace = true)
{ {
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return null; if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return null;
if (_tiles.TryGetValue(indices, out var tile)) return tile; if (_tiles.TryGetValue(indices, out var tile)) return tile;
// We don't have that 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}); return new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.TCMB}, true);
space.Air.MarkImmutable();
return space;
} }
return null; return null;
@@ -378,23 +422,21 @@ namespace Content.Server.GameObjects.Components.Atmos
return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty; return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty;
} }
public Dictionary<Direction, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false) public Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false)
{ {
var sides = new Dictionary<Direction, TileAtmosphere>(); var sides = new Dictionary<AtmosDirection, TileAtmosphere>();
foreach (var dir in Cardinal) 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); var tile = GetTile(side);
if (tile != null && (tile.Air != null || includeAirBlocked)) if (tile != null && (tile.Air != null || includeAirBlocked))
sides[dir] = tile; sides[direction] = tile;
} }
return sides; return sides;
} }
/// <inheritdoc />
public int HighPressureDeltaCount => _highPressureDelta.Count;
public long EqualizationQueueCycleControl { get; set; } public long EqualizationQueueCycleControl { get; set; }
/// <inheritdoc /> /// <inheritdoc />
@@ -422,35 +464,83 @@ namespace Content.Server.GameObjects.Components.Atmos
switch (_state) switch (_state)
{ {
case ProcessState.TileEqualize: case ProcessState.TileEqualize:
ProcessTileEqualize(); if (!ProcessTileEqualize(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.ActiveTiles; _state = ProcessState.ActiveTiles;
return; return;
case ProcessState.ActiveTiles: case ProcessState.ActiveTiles:
ProcessActiveTiles(); if (!ProcessActiveTiles(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.ExcitedGroups; _state = ProcessState.ExcitedGroups;
return; return;
case ProcessState.ExcitedGroups: case ProcessState.ExcitedGroups:
ProcessExcitedGroups(); if (!ProcessExcitedGroups(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.HighPressureDelta; _state = ProcessState.HighPressureDelta;
return; return;
case ProcessState.HighPressureDelta: case ProcessState.HighPressureDelta:
ProcessHighPressureDelta(); if (!ProcessHighPressureDelta(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.Hotspots; _state = ProcessState.Hotspots;
break; break;
case ProcessState.Hotspots: case ProcessState.Hotspots:
ProcessHotspots(); if (!ProcessHotspots(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.Superconductivity; _state = ProcessState.Superconductivity;
break; break;
case ProcessState.Superconductivity: case ProcessState.Superconductivity:
ProcessSuperconductivity(); if (!ProcessSuperconductivity(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.PipeNet; _state = ProcessState.PipeNet;
break; break;
case ProcessState.PipeNet: case ProcessState.PipeNet:
ProcessPipeNets(); if (!ProcessPipeNets(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.PipeNetDevices; _state = ProcessState.PipeNetDevices;
break; break;
case ProcessState.PipeNetDevices: case ProcessState.PipeNetDevices:
ProcessPipeNetDevices(); if (!ProcessPipeNetDevices(_paused))
{
_paused = true;
return;
}
_paused = false;
_state = ProcessState.TileEqualize; _state = ProcessState.TileEqualize;
break; break;
} }
@@ -458,47 +548,71 @@ namespace Content.Server.GameObjects.Components.Atmos
UpdateCounter++; UpdateCounter++;
} }
public void ProcessTileEqualize() public bool ProcessTileEqualize(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunTiles = new Queue<TileAtmosphere>(_activeTiles);
var number = 0; var number = 0;
foreach (var tile in _activeTiles.ToArray()) while (_currentRunTiles.Count > 0)
{ {
var tile = _currentRunTiles.Dequeue();
tile.EqualizePressureInZone(UpdateCounter); tile.EqualizePressureInZone(UpdateCounter);
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
public void ProcessActiveTiles() _tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
public bool ProcessActiveTiles(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunTiles = new Queue<TileAtmosphere>(_activeTiles);
var number = 0; var number = 0;
foreach (var tile in _activeTiles.ToArray()) while (_currentRunTiles.Count > 0)
{ {
var tile = _currentRunTiles.Dequeue();
tile.ProcessCell(UpdateCounter); tile.ProcessCell(UpdateCounter);
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
public void ProcessExcitedGroups() _activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
public bool ProcessExcitedGroups(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunExcitedGroups = new Queue<ExcitedGroup>(_excitedGroups);
var number = 0; var number = 0;
foreach (var excitedGroup in _excitedGroups.ToArray()) while (_currentRunExcitedGroups.Count > 0)
{ {
var excitedGroup = _currentRunExcitedGroups.Dequeue();
excitedGroup.BreakdownCooldown++; excitedGroup.BreakdownCooldown++;
excitedGroup.DismantleCooldown++; excitedGroup.DismantleCooldown++;
@@ -512,17 +626,27 @@ namespace Content.Server.GameObjects.Components.Atmos
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
public void ProcessHighPressureDelta() _excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
public bool ProcessHighPressureDelta(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunTiles = new Queue<TileAtmosphere>(_highPressureDelta);
var number = 0; var number = 0;
foreach (var tile in _highPressureDelta.ToArray()) while (_currentRunTiles.Count > 0)
{ {
var tile = _currentRunTiles.Dequeue();
tile.HighPressureMovements(); tile.HighPressureMovements();
tile.PressureDifference = 0f; tile.PressureDifference = 0f;
tile.PressureSpecificTarget = null; tile.PressureSpecificTarget = null;
@@ -532,83 +656,124 @@ namespace Content.Server.GameObjects.Components.Atmos
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
private void ProcessHotspots() _highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
private bool ProcessHotspots(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunTiles = new Queue<TileAtmosphere>(_hotspotTiles);
var number = 0; var number = 0;
foreach (var hotspot in _hotspotTiles.ToArray()) while (_currentRunTiles.Count > 0)
{ {
var hotspot = _currentRunTiles.Dequeue();
hotspot.ProcessHotspot(); hotspot.ProcessHotspot();
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
private void ProcessSuperconductivity() _hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
private bool ProcessSuperconductivity(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunTiles = new Queue<TileAtmosphere>(_superconductivityTiles);
var number = 0; var number = 0;
foreach (var superconductivity in _superconductivityTiles.ToArray()) while (_currentRunTiles.Count > 0)
{ {
var superconductivity = _currentRunTiles.Dequeue();
superconductivity.Superconduct(); superconductivity.Superconduct();
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
} }
private void ProcessPipeNets() _superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
private bool ProcessPipeNets(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunPipeNet = new Queue<IPipeNet>(_pipeNets);
var number = 0; var number = 0;
var pipeNets = _pipeNets.ToArray(); while (_currentRunPipeNet.Count > 0)
var netCount = pipeNets.Count();
for ( ; _pipeNetIndex < netCount; _pipeNetIndex++)
{ {
pipeNets[_pipeNetIndex].Update(); var pipenet = _currentRunPipeNet.Dequeue();
pipenet.Update();
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
return; {
_pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false;
} }
_pipeNetIndex = 0;
} }
private void ProcessPipeNetDevices() _pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true;
}
private bool ProcessPipeNetDevices(bool resumed = false)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed)
_currentRunPipeNetDevice = new Queue<PipeNetDeviceComponent>(_pipeNetDevices);
var number = 0; var number = 0;
var pipeNetDevices = _pipeNetDevices.ToArray(); while (_currentRunPipeNet.Count > 0)
var deviceCount = pipeNetDevices.Count();
for ( ; _deviceIndex < deviceCount; _deviceIndex++)
{ {
pipeNetDevices[_deviceIndex].Update(); var device = _currentRunPipeNetDevice.Dequeue();
device.Update();
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds) 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) private AirtightComponent? GetObstructingComponent(MapIndices indices)
{ {
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default; if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
@@ -622,12 +787,6 @@ namespace Content.Server.GameObjects.Components.Atmos
return null; return null;
} }
private static readonly Direction[] Cardinal =
new []
{
Direction.North, Direction.East, Direction.South, Direction.West
};
public void Dispose() public void Dispose()
{ {

View File

@@ -1,25 +1,50 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Server.Atmos.Reactions;
using Content.Server.Interfaces;
using Content.Shared.GameObjects.EntitySystems.Atmos; using Content.Shared.GameObjects.EntitySystems.Atmos;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing; using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Map; using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class AtmosphereSystem : SharedAtmosphereSystem public class AtmosphereSystem : SharedAtmosphereSystem
{ {
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPauseManager _pauseManager = default!; [Dependency] private readonly IPauseManager _pauseManager = default!;
[Dependency] private IEntityManager _entityManager = default!;
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
/// <summary>
/// List of gas reactions ordered by priority.
/// </summary>
public IEnumerable<GasReactionPrototype> GasReactions => _gasReactions!;
/// <summary>
/// EventBus reference for gas reactions.
/// </summary>
public IEventBus EventBus => _entityManager.EventBus;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray();
Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority));
_mapManager.TileChanged += OnTileChanged; _mapManager.TileChanged += OnTileChanged;
} }

View File

@@ -1,12 +1,13 @@
#nullable enable #nullable enable
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Server.Atmos.Reactions; using Content.Server.Atmos.Reactions;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Interfaces.Serialization;
namespace Content.Server.Interfaces namespace Content.Server.Interfaces
{ {
public interface IGasReactionEffect : IExposeData public interface IGasReactionEffect : IExposeData
{ {
ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder); ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus);
} }
} }

View File

@@ -0,0 +1,89 @@
using System;
using Robust.Shared.Maths;
namespace Content.Shared.Atmos
{
/// <summary>
/// The reason we use this over <see cref="Direction"/> is that we are going to do some heavy bitflag usage.
/// </summary>
[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;
}
}
}

View File

@@ -211,6 +211,12 @@
public const int LowPressureDamage = 4; public const int LowPressureDamage = 4;
public const float WindowHeatTransferCoefficient = 0.1f; public const float WindowHeatTransferCoefficient = 0.1f;
/// <summary>
/// Directions that atmos currently supports. Modify in case of multi-z.
/// See <see cref="AtmosDirection"/> on the server.
/// </summary>
public const int Directions = 4;
} }
/// <summary> /// <summary>

View File

@@ -84,6 +84,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Occluders/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Occluders/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Patreon/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Patreon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Phoron/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Phoron/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pipenet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=placeable/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=placeable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=preemptively/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=prefs/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=prefs/@EntryIndexedValue">True</s:Boolean>
@@ -96,6 +97,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Spawner/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Spawner/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Strippable/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Strippable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=stunnable/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=stunnable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Superconduct/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=superconduction/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=superconduction/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=swsl/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=swsl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=underplating/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=underplating/@EntryIndexedValue">True</s:Boolean>