ECS Atmos Part 5: Moves all logic from GridAtmosphereComponent to AtmosphereSystem. (#4331)

This commit is contained in:
Vera Aguilera Puerto
2021-07-23 11:09:01 +02:00
committed by GitHub
parent 354ef6daf3
commit 4112847142
23 changed files with 1242 additions and 1355 deletions

View File

@@ -1,24 +1,13 @@
// ReSharper disable once RedundantUsingDirective
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.CPUJob.JobQueues.Queues;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos;
using Content.Shared.Maps;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
using Dependency = Robust.Shared.IoC.DependencyAttribute;
@@ -31,33 +20,21 @@ namespace Content.Server.Atmos.Components
[RegisterComponent, Serializable]
public class GridAtmosphereComponent : Component, IGridAtmosphereComponent, ISerializationHooks
{
[Dependency] private IMapManager _mapManager = default!;
[Dependency] private ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private IServerEntityManager _serverEntityManager = default!;
[Dependency] private IGameTiming _gameTiming = default!;
internal GasTileOverlaySystem GasTileOverlaySystem { get; private set; } = default!;
public AtmosphereSystem AtmosphereSystem { get; private set; } = default!;
public override string Name => "GridAtmosphere";
public bool ProcessingPaused { get; set; } = false;
public float Timer { get; set; }
private GridId _gridId;
[ComponentDependency] private IMapGridComponent? _mapGridComponent;
public virtual bool Simulated => true;
[ViewVariables]
public bool RevalidatePaused { get; set; } = false;
[ViewVariables]
public bool ProcessingPaused { get; set; } = false;
[ViewVariables]
public float Timer { get; set; } = 0f;
[ViewVariables]
public int UpdateCounter { get; set; } = 0;
[ViewVariables]
public readonly HashSet<ExcitedGroup> ExcitedGroups = new(1000);
[ViewVariables]
public int ExcitedGroupCount => ExcitedGroups.Count;
[DataField("uniqueMixes")]
public List<GasMixture>? UniqueMixes;
@@ -73,6 +50,12 @@ namespace Content.Server.Atmos.Components
[ViewVariables]
public int ActiveTilesCount => ActiveTiles.Count;
[ViewVariables]
public readonly HashSet<ExcitedGroup> ExcitedGroups = new(1000);
[ViewVariables]
public int ExcitedGroupCount => ExcitedGroups.Count;
[ViewVariables]
public readonly HashSet<TileAtmosphere> HotspotTiles = new(1000);
@@ -85,9 +68,6 @@ namespace Content.Server.Atmos.Components
[ViewVariables]
public int SuperconductivityTilesCount => SuperconductivityTiles.Count;
[ViewVariables]
public readonly HashSet<Vector2i> InvalidatedCoords = new(1000);
[ViewVariables]
public HashSet<TileAtmosphere> HighPressureDelta = new(1000);
@@ -112,22 +92,21 @@ namespace Content.Server.Atmos.Components
[ViewVariables]
public Queue<AtmosDeviceComponent> CurrentRunAtmosDevices = new();
[ViewVariables]
public readonly HashSet<Vector2i> InvalidatedCoords = new(1000);
[ViewVariables]
public Queue<Vector2i> CurrentRunInvalidatedCoordinates = new();
[ViewVariables]
public int InvalidatedCoordsCount => InvalidatedCoords.Count;
[ViewVariables]
public long EqualizationQueueCycleControl { get; set; }
[ViewVariables]
public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.TileEqualize;
public GridAtmosphereComponent()
{
ProcessingPaused = false;
}
/// <inheritdoc />
public virtual void PryTile(Vector2i indices)
{
if (IsSpace(indices) || IsAirBlocked(indices)) return;
indices.PryTile(_gridId, _mapManager, _tileDefinitionManager, _serverEntityManager);
}
void ISerializationHooks.BeforeSerialization()
{
var uniqueMixes = new List<GasMixture>();
@@ -156,403 +135,5 @@ namespace Content.Server.Atmos.Components
UniqueMixes = uniqueMixes;
TilesUniqueMixes = tiles;
}
protected override void Initialize()
{
base.Initialize();
Tiles.Clear();
if (TilesUniqueMixes != null && Owner.TryGetComponent(out IMapGridComponent? mapGrid))
{
foreach (var (indices, mix) in TilesUniqueMixes)
{
try
{
Tiles.Add(indices, new TileAtmosphere(this, mapGrid.GridIndex, indices, (GasMixture) UniqueMixes![mix].Clone()));
}
catch (ArgumentOutOfRangeException)
{
Logger.Error($"Error during atmos serialization! Tile at {indices} points to an unique mix ({mix}) out of range!");
throw;
}
Invalidate(indices);
}
}
GasTileOverlaySystem = EntitySystem.Get<GasTileOverlaySystem>();
AtmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
RepopulateTiles();
}
protected override void OnAdd()
{
base.OnAdd();
if (Owner.TryGetComponent(out IMapGridComponent? mapGrid))
_gridId = mapGrid.GridIndex;
}
public virtual void RepopulateTiles()
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
foreach (var tile in mapGrid.Grid.GetAllTiles())
{
if(!Tiles.ContainsKey(tile.GridIndices))
Tiles.Add(tile.GridIndices, new TileAtmosphere(this, tile.GridIndex, tile.GridIndices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C}));
Invalidate(tile.GridIndices);
}
foreach (var (_, tile) in Tiles.ToArray())
{
tile.UpdateAdjacent();
tile.UpdateVisuals();
}
}
/// <inheritdoc />
public virtual void Invalidate(Vector2i indices)
{
InvalidatedCoords.Add(indices);
}
public virtual void Revalidate()
{
foreach (var indices in InvalidatedCoords)
{
var tile = GetTile(indices);
if (tile == null)
{
tile = new TileAtmosphere(this, _gridId, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
Tiles[indices] = tile;
}
var isAirBlocked = IsAirBlocked(indices);
if (IsSpace(indices) && !isAirBlocked)
{
tile.Air = new GasMixture(GetVolumeForCells(1));
tile.Air.MarkImmutable();
Tiles[indices] = tile;
} else if (isAirBlocked)
{
var nullAir = false;
foreach (var airtight in GetObstructingComponents(indices))
{
if (airtight.NoAirWhenFullyAirBlocked)
{
nullAir = true;
break;
}
}
if(nullAir)
tile.Air = null;
}
else
{
if (tile.Air == null && NeedsVacuumFixing(indices))
{
FixVacuum(tile.GridIndices);
}
// Tile used to be space, but isn't anymore.
if (tile.Air?.Immutable ?? false)
{
tile.Air = null;
}
tile.Air ??= new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
}
// By removing the active tile, we effectively remove its excited group, if any.
RemoveActiveTile(tile);
// Then we activate the tile again.
AddActiveTile(tile);
tile.BlockedAirflow = GetBlockedDirections(indices);
// TODO ATMOS: Query all the contents of this tile (like walls) and calculate the correct thermal conductivity
tile.ThermalConductivity = tile.Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.5f;
tile.UpdateAdjacent();
GasTileOverlaySystem.Invalidate(_gridId, indices);
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
var otherIndices = indices.Offset(direction.ToDirection());
var otherTile = GetTile(otherIndices);
if (otherTile != null) AddActiveTile(otherTile);
}
}
InvalidatedCoords.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UpdateAdjacentBits(Vector2i indices)
{
GetTile(indices)?.UpdateAdjacent();
}
/// <inheritdoc />
public virtual void FixVacuum(Vector2i indices)
{
var tile = GetTile(indices);
if (tile?.GridIndex != _gridId) return;
// includeAirBlocked is false, therefore all tiles in this have Air != null.
var adjacent = GetAdjacentTiles(indices);
tile.Air = new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
Tiles[indices] = tile;
var ratio = 1f / adjacent.Count;
foreach (var (_, adj) in adjacent)
{
var mix = adj.Air!.RemoveRatio(ratio);
AtmosphereSystem.Merge(tile.Air, mix);
AtmosphereSystem.Merge(adj.Air, mix);
}
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void AddActiveTile(TileAtmosphere tile)
{
if (tile?.GridIndex != _gridId || tile.Air == null) return;
tile.Excited = true;
ActiveTiles.Add(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void RemoveActiveTile(TileAtmosphere tile, bool disposeGroup = true)
{
ActiveTiles.Remove(tile);
tile.Excited = false;
if(disposeGroup)
tile.ExcitedGroup?.Dispose();
else
tile.ExcitedGroup?.RemoveTile(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void AddHotspotTile(TileAtmosphere tile)
{
if (tile?.GridIndex != _gridId || tile?.Air == null) return;
HotspotTiles.Add(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void RemoveHotspotTile(TileAtmosphere tile)
{
HotspotTiles.Remove(tile);
}
public virtual void AddSuperconductivityTile(TileAtmosphere tile)
{
if (tile?.GridIndex != _gridId || !AtmosphereSystem.Superconduction) return;
SuperconductivityTiles.Add(tile);
}
public virtual void RemoveSuperconductivityTile(TileAtmosphere tile)
{
SuperconductivityTiles.Remove(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void AddHighPressureDelta(TileAtmosphere tile)
{
if (tile.GridIndex != _gridId) return;
HighPressureDelta.Add(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual bool HasHighPressureDelta(TileAtmosphere tile)
{
return HighPressureDelta.Contains(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void AddExcitedGroup(ExcitedGroup excitedGroup)
{
ExcitedGroups.Add(excitedGroup);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void RemoveExcitedGroup(ExcitedGroup excitedGroup)
{
ExcitedGroups.Remove(excitedGroup);
}
public virtual void AddPipeNet(IPipeNet pipeNet)
{
PipeNets.Add(pipeNet);
}
public virtual void RemovePipeNet(IPipeNet pipeNet)
{
PipeNets.Remove(pipeNet);
}
public virtual void AddAtmosDevice(AtmosDeviceComponent atmosDevice)
{
AtmosDevices.Add(atmosDevice);
}
public virtual void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice)
{
AtmosDevices.Remove(atmosDevice);
}
/// <inheritdoc />
public virtual TileAtmosphere? GetTile(EntityCoordinates coordinates, bool createSpace = true)
{
return GetTile(coordinates.ToVector2i(_serverEntityManager, _mapManager), createSpace);
}
/// <inheritdoc />
public virtual TileAtmosphere? GetTile(Vector2i indices, bool createSpace = true)
{
if (Tiles.TryGetValue(indices, out var tile)) return tile;
// We don't have that tile!
if (IsSpace(indices) && createSpace)
{
return new TileAtmosphere(this, _gridId, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.TCMB}, true);
}
return null;
}
/// <inheritdoc />
public bool IsAirBlocked(Vector2i indices, AtmosDirection direction = AtmosDirection.All)
{
var directions = AtmosDirection.Invalid;
foreach (var obstructingComponent in GetObstructingComponents(indices))
{
if (!obstructingComponent.AirBlocked)
continue;
// We set the directions that are air-blocked so far,
// as you could have a full obstruction with only 4 directional air blockers.
directions |= obstructingComponent.AirBlockedDirection;
if (directions.IsFlagSet(direction))
return true;
}
return false;
}
/// <inheritdoc />
public virtual bool IsSpace(Vector2i indices)
{
if (_mapGridComponent == null) return default;
return _mapGridComponent.Grid.GetTileRef(indices).IsSpace();
}
public Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(EntityCoordinates coordinates, bool includeAirBlocked = false)
{
return GetAdjacentTiles(coordinates.ToVector2i(_serverEntityManager, _mapManager), includeAirBlocked);
}
public Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(Vector2i indices, bool includeAirBlocked = false)
{
var sides = new Dictionary<AtmosDirection, TileAtmosphere>();
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
var side = indices.Offset(direction.ToDirection());
var tile = GetTile(side);
if (tile != null && (tile.Air != null || includeAirBlocked))
sides[direction] = tile;
}
return sides;
}
public long EqualizationQueueCycleControl { get; set; }
/// <inheritdoc />
public float GetVolumeForCells(int cellCount)
{
if (_mapGridComponent == null) return default;
return _mapGridComponent.Grid.TileSize * cellCount * Atmospherics.CellVolume;
}
protected virtual IEnumerable<AirtightComponent> GetObstructingComponents(Vector2i indices)
{
var gridLookup = EntitySystem.Get<GridTileLookupSystem>();
foreach (var v in gridLookup.GetEntitiesIntersecting(_gridId, indices))
{
if (v.TryGetComponent<AirtightComponent>(out var ac))
yield return ac;
}
}
private bool NeedsVacuumFixing(Vector2i indices)
{
var value = false;
foreach (var airtightComponent in GetObstructingComponents(indices))
{
value |= airtightComponent.FixVacuum;
}
return value;
}
private AtmosDirection GetBlockedDirections(Vector2i indices)
{
var value = AtmosDirection.Invalid;
foreach (var airtightComponent in GetObstructingComponents(indices))
{
if(airtightComponent.AirBlocked)
value |= airtightComponent.AirBlockedDirection;
}
return value;
}
public void Dispose()
{
}
public IEnumerator<TileAtmosphere> GetEnumerator()
{
return Tiles.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <inheritdoc />
public virtual void BurnTile(Vector2i gridIndices)
{
// TODO ATMOS
}
}
}

View File

@@ -1,181 +1,12 @@
using System.Collections.Generic;
using Content.Server.Atmos.Piping.Components;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.GameObjects;
namespace Content.Server.Atmos.Components
{
public interface IGridAtmosphereComponent : IComponent, IEnumerable<TileAtmosphere>
public interface IGridAtmosphereComponent : IComponent
{
/// <summary>
/// Whether this atmosphere is simulated or not.
/// </summary>
bool Simulated { get; }
/// <summary>
/// Number of times <see cref="Update"/> has been called.
/// </summary>
int UpdateCounter { get; }
/// <summary>
/// Control variable for equalization.
/// </summary>
long EqualizationQueueCycleControl { get; set; }
/// <summary>
/// Attemps to pry a tile.
/// </summary>
/// <param name="indices"></param>
void PryTile(Vector2i indices);
/// <summary>
/// Burns a tile.
/// </summary>
/// <param name="gridIndices"></param>
void BurnTile(Vector2i gridIndices);
/// <summary>
/// Invalidates a coordinate to be revalidated again.
/// Use this after changing a tile's gas contents, or when the tile becomes space, etc.
/// </summary>
/// <param name="indices"></param>
void Invalidate(Vector2i indices);
/// <summary>
/// Attempts to fix a sudden vacuum by creating gas.
/// </summary>
void FixVacuum(Vector2i indices);
/// <summary>
/// Revalidates indices immediately.
/// </summary>
/// <param name="indices"></param>
void UpdateAdjacentBits(Vector2i indices);
/// <summary>
/// Adds an active tile so it becomes processed every update until it becomes inactive.
/// Also makes the tile excited.
/// </summary>
/// <param name="tile"></param>
void AddActiveTile(TileAtmosphere tile);
/// <summary>
/// Removes an active tile and disposes of its <seealso cref="ExcitedGroup"/>.
/// Use with caution.
/// </summary>
/// <param name="tile"></param>
void RemoveActiveTile(TileAtmosphere tile, bool disposeGroup = true);
/// <summary>
/// Marks a tile as having a hotspot so it can be processed.
/// </summary>
/// <param name="tile"></param>
void AddHotspotTile(TileAtmosphere tile);
/// <summary>
/// Removes a tile from the hotspot processing list.
/// </summary>
/// <param name="tile"></param>
void RemoveHotspotTile(TileAtmosphere tile);
/// <summary>
/// Marks a tile as superconductive so it can be processed.
/// </summary>
/// <param name="tile"></param>
void AddSuperconductivityTile(TileAtmosphere tile);
/// <summary>
/// Removes a tile from the superconductivity processing list.
/// </summary>
/// <param name="tile"></param>
void RemoveSuperconductivityTile(TileAtmosphere tile);
/// <summary>
/// Marks a tile has having high pressure differences that need to be equalized.
/// </summary>
/// <param name="tile"></param>
void AddHighPressureDelta(TileAtmosphere tile);
/// <summary>
/// Returns whether the tile in question is marked as having high pressure differences or not.
/// </summary>
/// <param name="tile"></param>
/// <returns></returns>
bool HasHighPressureDelta(TileAtmosphere tile);
/// <summary>
/// Adds a excited group to be processed.
/// </summary>
/// <param name="excitedGroup"></param>
void AddExcitedGroup(ExcitedGroup excitedGroup);
/// <summary>
/// Removes an excited group.
/// </summary>
/// <param name="excitedGroup"></param>
void RemoveExcitedGroup(ExcitedGroup excitedGroup);
/// <summary>
/// Returns a tile.
/// </summary>
/// <param name="indices"></param>
/// <param name="createSpace"></param>
/// <returns></returns>
TileAtmosphere? GetTile(Vector2i indices, bool createSpace = true);
/// <summary>
/// Returns a tile.
/// </summary>
/// <param name="coordinates"></param>
/// <param name="createSpace"></param>
/// <returns></returns>
TileAtmosphere? GetTile(EntityCoordinates coordinates, bool createSpace = true);
/// <summary>
/// Returns if the tile in question is air-blocked.
/// This could be due to a wall, an airlock, etc.
/// <seealso cref="AirtightComponent"/>
/// </summary>
/// <param name="indices"></param>
/// <param name="direction"></param>
/// <returns></returns>
bool IsAirBlocked(Vector2i indices, AtmosDirection direction);
/// <summary>
/// Returns if the tile in question is space.
/// </summary>
/// <param name="indices"></param>
/// <returns></returns>
bool IsSpace(Vector2i indices);
/// <summary>
/// Returns the volume in liters for a number of cells/tiles.
/// </summary>
/// <param name="cellCount"></param>
/// <returns></returns>
float GetVolumeForCells(int cellCount);
void RepopulateTiles();
/// <summary>
/// Returns a dictionary of adjacent TileAtmospheres.
/// </summary>
Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(EntityCoordinates coordinates, bool includeAirBlocked = false);
/// <summary>
/// Returns a dictionary of adjacent TileAtmospheres.
/// </summary>
Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(Vector2i indices, bool includeAirBlocked = false);
void AddPipeNet(IPipeNet pipeNet);
void RemovePipeNet(IPipeNet pipeNet);
void AddAtmosDevice(AtmosDeviceComponent atmosDevice);
void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice);
}
}

View File

@@ -12,22 +12,5 @@ namespace Content.Server.Atmos.Components
public class SpaceGridAtmosphereComponent : UnsimulatedGridAtmosphereComponent
{
public override string Name => "SpaceGridAtmosphere";
public override void RepopulateTiles() { }
public override bool IsSpace(Vector2i indices)
{
return true;
}
public override TileAtmosphere GetTile(Vector2i indices, bool createSpace = true)
{
return new(this, GridId.Invalid, indices, new GasMixture(Atmospherics.CellVolume), true);
}
protected override IEnumerable<AirtightComponent> GetObstructingComponents(Vector2i indices)
{
return Enumerable.Empty<AirtightComponent>();
}
}
}

View File

@@ -16,55 +16,5 @@ namespace Content.Server.Atmos.Components
public override string Name => "UnsimulatedGridAtmosphere";
public override bool Simulated => false;
public override void PryTile(Vector2i indices) { }
public override void RepopulateTiles()
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
foreach (var tile in mapGrid.Grid.GetAllTiles())
{
if(!Tiles.ContainsKey(tile.GridIndices))
Tiles.Add(tile.GridIndices, new TileAtmosphere(this, tile.GridIndex, tile.GridIndices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C}));
}
}
public override void Invalidate(Vector2i indices) { }
public override void Revalidate() { }
public override void FixVacuum(Vector2i indices) { }
public override void AddActiveTile(TileAtmosphere? tile) { }
public override void RemoveActiveTile(TileAtmosphere? tile, bool disposeGroup = true) { }
public override void AddHotspotTile(TileAtmosphere? tile) { }
public override void RemoveHotspotTile(TileAtmosphere? tile) { }
public override void AddSuperconductivityTile(TileAtmosphere? tile) { }
public override void RemoveSuperconductivityTile(TileAtmosphere? tile) { }
public override void AddHighPressureDelta(TileAtmosphere? tile) { }
public override bool HasHighPressureDelta(TileAtmosphere tile)
{
return false;
}
public override void AddExcitedGroup(ExcitedGroup excitedGroup) { }
public override void RemoveExcitedGroup(ExcitedGroup excitedGroup) { }
public override void AddPipeNet(IPipeNet pipeNet) { }
public override void RemovePipeNet(IPipeNet pipeNet) { }
public override void AddAtmosDevice(AtmosDeviceComponent atmosDevice) { }
public override void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice) { }
}
}

View File

@@ -127,6 +127,7 @@ namespace Content.Server.Atmos.EntitySystems
AccumulatedFrameTime -= _updateCooldown;
var currentTick = _gameTiming.CurTick;
var atmosphereSystem = Get<AtmosphereSystem>();
// Now we'll go through each player, then through each chunk in range of that player checking if the player is still in range
// If they are, check if they need the new data to send (i.e. if there's an overlay for the gas).
@@ -156,7 +157,7 @@ namespace Content.Server.Atmos.EntitySystems
for (var x = 0; x < LocalViewRange; x++)
{
var Vector2i = new Vector2i(baseTile.X + x, baseTile.Y + y);
debugOverlayContent[index++] = ConvertTileToData(gam.GetTile(Vector2i));
debugOverlayContent[index++] = ConvertTileToData(atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, gam, Vector2i));
}
}

View File

@@ -1,9 +1,13 @@
using Content.Shared.CCVar;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
namespace Content.Server.Atmos.EntitySystems
{
public partial class AtmosphereSystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
public bool SpaceWind { get; private set; }
public string? SpaceWindSound { get; private set; }
public bool MonstermosEqualization { get; private set; }

View File

@@ -0,0 +1,137 @@
using Content.Server.Atmos.Components;
using Content.Shared.Atmos;
using Robust.Shared.Utility;
namespace Content.Server.Atmos.EntitySystems
{
public partial class AtmosphereSystem
{
private void ExcitedGroupAddTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
{
DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
DebugTools.Assert(tile.ExcitedGroup == null, "Tried to add a tile to an excited group when it's already in another one!");
excitedGroup.Tiles.Add(tile);
tile.ExcitedGroup = excitedGroup;
ExcitedGroupResetCooldowns(excitedGroup);
}
private void ExcitedGroupRemoveTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
{
DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
DebugTools.Assert(tile.ExcitedGroup == excitedGroup, "Tried to remove a tile from an excited group it's not present in!");
tile.ExcitedGroup = null;
excitedGroup.Tiles.Remove(tile);
}
private void ExcitedGroupMerge(GridAtmosphereComponent gridAtmosphere, ExcitedGroup ourGroup, ExcitedGroup otherGroup)
{
DebugTools.Assert(!ourGroup.Disposed, "Excited group is disposed!");
DebugTools.Assert(!otherGroup.Disposed, "Excited group is disposed!");
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(ourGroup), "Grid Atmosphere does not contain Excited Group!");
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(otherGroup), "Grid Atmosphere does not contain Excited Group!");
var ourSize = ourGroup.Tiles.Count;
var otherSize = otherGroup.Tiles.Count;
ExcitedGroup winner;
ExcitedGroup loser;
if (ourSize > otherSize)
{
winner = ourGroup;
loser = otherGroup;
}
else
{
winner = otherGroup;
loser = ourGroup;
}
foreach (var tile in loser.Tiles)
{
tile.ExcitedGroup = winner;
winner.Tiles.Add(tile);
}
loser.Tiles.Clear();
ExcitedGroupDispose(gridAtmosphere, loser);
ExcitedGroupResetCooldowns(winner);
}
private void ExcitedGroupResetCooldowns(ExcitedGroup excitedGroup)
{
DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
excitedGroup.BreakdownCooldown = 0;
excitedGroup.DismantleCooldown = 0;
}
private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup, bool spaceIsAllConsuming = false)
{
DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
var combined = new GasMixture(Atmospherics.CellVolume);
var tileSize = excitedGroup.Tiles.Count;
if (excitedGroup.Disposed) return;
if (tileSize == 0)
{
ExcitedGroupDispose(gridAtmosphere, excitedGroup);
return;
}
foreach (var tile in excitedGroup.Tiles)
{
if (tile?.Air == null)
continue;
Merge(combined, tile.Air);
if (!spaceIsAllConsuming || !tile.Air.Immutable)
continue;
combined.Clear();
break;
}
combined.Multiply(1 / (float)tileSize);
foreach (var tile in excitedGroup.Tiles)
{
if (tile?.Air == null) continue;
tile.Air.CopyFromMutable(combined);
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
}
excitedGroup.BreakdownCooldown = 0;
}
private void ExcitedGroupDismantle(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup, bool unexcite = true)
{
foreach (var tile in excitedGroup.Tiles)
{
tile.ExcitedGroup = null;
if (!unexcite)
continue;
RemoveActiveTile(gridAtmosphere, tile);
}
excitedGroup.Tiles.Clear();
}
private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
{
if (excitedGroup.Disposed)
return;
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
excitedGroup.Disposed = true;
gridAtmosphere.ExcitedGroups.Remove(excitedGroup);
ExcitedGroupDismantle(gridAtmosphere, excitedGroup, false);
}
}
}

View File

@@ -4,12 +4,16 @@ using System.Linq;
using Content.Server.Atmos.Reactions;
using Content.Server.Interfaces;
using Content.Shared.Atmos;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.Server.Atmos.EntitySystems
{
public partial class AtmosphereSystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
@@ -51,16 +55,23 @@ namespace Content.Server.Atmos.EntitySystems
return mixture.Temperature * GetHeatCapacity(mixture);
}
public float GetThermalEnergy(GasMixture mixture, float cachedHeatCapacity)
{
return mixture.Temperature * cachedHeatCapacity;
}
public void Merge(GasMixture receiver, GasMixture giver)
{
if (receiver.Immutable) return;
if (MathF.Abs(receiver.Temperature - giver.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider)
{
var combinedHeatCapacity = GetHeatCapacity(receiver) + GetHeatCapacity(giver);
var receiverHeatCapacity = GetHeatCapacity(receiver);
var giverHeatCapacity = GetHeatCapacity(giver);
var combinedHeatCapacity = receiverHeatCapacity + giverHeatCapacity;
if (combinedHeatCapacity > 0f)
{
receiver.Temperature = (GetThermalEnergy(giver) + GetThermalEnergy(receiver)) / combinedHeatCapacity;
receiver.Temperature = (GetThermalEnergy(giver, giverHeatCapacity) + GetThermalEnergy(receiver, receiverHeatCapacity)) / combinedHeatCapacity;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -58,7 +58,7 @@ namespace Content.Server.Atmos.EntitySystems
private void ConsiderPressureDifference(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other, float difference)
{
gridAtmosphere.AddHighPressureDelta(tile);
gridAtmosphere.HighPressureDelta.Add(tile);
if (difference > tile.PressureDifference)
{
tile.PressureDifference = difference;

View File

@@ -4,8 +4,8 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.Reactions;
using Content.Server.Coordinates.Helpers;
using Content.Shared.Atmos;
using Content.Shared.GameTicking;
using Content.Shared.Maps;
using Robust.Shared.Map;
namespace Content.Server.Atmos.EntitySystems
{
@@ -15,13 +15,13 @@ namespace Content.Server.Atmos.EntitySystems
{
if (!tile.Hotspot.Valid)
{
gridAtmosphere.RemoveHotspotTile(tile);
gridAtmosphere.HotspotTiles.Remove(tile);
return;
}
if (!tile.Excited)
{
gridAtmosphere.AddActiveTile(tile);
AddActiveTile(gridAtmosphere, tile);
}
if (!tile.Hotspot.SkippedFirstProcess)
@@ -30,13 +30,14 @@ namespace Content.Server.Atmos.EntitySystems
return;
}
tile.ExcitedGroup?.ResetCooldowns();
if(tile.ExcitedGroup != null)
ExcitedGroupResetCooldowns(tile.ExcitedGroup);
if ((tile.Hotspot.Temperature < Atmospherics.FireMinimumTemperatureToExist) || (tile.Hotspot.Volume <= 1f)
|| tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f))
{
tile.Hotspot = new Hotspot();
tile.UpdateVisuals();
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
return;
}
@@ -45,13 +46,17 @@ namespace Content.Server.Atmos.EntitySystems
if (tile.Hotspot.Bypassing)
{
tile.Hotspot.State = 3;
gridAtmosphere.BurnTile(tile.GridIndices);
// TODO ATMOS: Burn tile here
if (tile.Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread)
{
var radiatedTemperature = tile.Air.Temperature * Atmospherics.FireSpreadRadiosityScale;
foreach (var otherTile in tile.AdjacentTiles)
{
// TODO ATMOS: This is sus. Suss this out.
if (otherTile == null)
continue;
if(!otherTile.Hotspot.Valid)
HotspotExpose(gridAtmosphere, otherTile, radiatedTemperature, Atmospherics.CellVolume/4);
}
@@ -108,8 +113,8 @@ namespace Content.Server.Atmos.EntitySystems
tile.Hotspot.Start();
gridAtmosphere.AddActiveTile(tile);
gridAtmosphere.AddHotspotTile(tile);
AddActiveTile(gridAtmosphere, tile);
gridAtmosphere.HotspotTiles.Add(tile);
}
}
@@ -128,11 +133,11 @@ namespace Content.Server.Atmos.EntitySystems
{
var affected = tile.Air.RemoveRatio(tile.Hotspot.Volume / tile.Air.Volume);
affected.Temperature = tile.Hotspot.Temperature;
gridAtmosphere.AtmosphereSystem.React(affected, tile);
React(affected, tile);
tile.Hotspot.Temperature = affected.Temperature;
tile.Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
Merge(tile.Air, affected);
gridAtmosphere.Invalidate(tile.GridIndices);
gridAtmosphere.InvalidatedCoords.Add(tile.GridIndices);
}
var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex);

View File

@@ -12,7 +12,7 @@ namespace Content.Server.Atmos.EntitySystems
// Can't process a tile without air
if (tile.Air == null)
{
gridAtmosphere.RemoveActiveTile(tile);
RemoveActiveTile(gridAtmosphere, tile);
return;
}
@@ -46,7 +46,7 @@ namespace Content.Server.Atmos.EntitySystems
{
if (tile.ExcitedGroup != enemyTile.ExcitedGroup)
{
tile.ExcitedGroup.MergeGroups(enemyTile.ExcitedGroup);
ExcitedGroupMerge(gridAtmosphere, tile.ExcitedGroup, enemyTile.ExcitedGroup);
}
shouldShareAir = true;
@@ -54,7 +54,7 @@ namespace Content.Server.Atmos.EntitySystems
{
if (!enemyTile.Excited)
{
gridAtmosphere.AddActiveTile(enemyTile);
AddActiveTile(gridAtmosphere, enemyTile);
}
var excitedGroup = tile.ExcitedGroup;
@@ -63,14 +63,14 @@ namespace Content.Server.Atmos.EntitySystems
if (excitedGroup == null)
{
excitedGroup = new ExcitedGroup();
excitedGroup.Initialize(gridAtmosphere);
gridAtmosphere.ExcitedGroups.Add(excitedGroup);
}
if (tile.ExcitedGroup == null)
excitedGroup.AddTile(tile);
ExcitedGroupAddTile(excitedGroup, tile);
if(enemyTile.ExcitedGroup == null)
excitedGroup.AddTile(enemyTile);
ExcitedGroupAddTile(excitedGroup, enemyTile);
shouldShareAir = true;
}
@@ -97,7 +97,8 @@ namespace Content.Server.Atmos.EntitySystems
if(tile.Air != null)
React(tile.Air, tile);
tile.UpdateVisuals();
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
var remove = true;
@@ -106,7 +107,7 @@ namespace Content.Server.Atmos.EntitySystems
remove = false;
if(tile.ExcitedGroup == null && remove)
gridAtmosphere.RemoveActiveTile(tile);
RemoveActiveTile(gridAtmosphere, tile);
}
private void Archive(TileAtmosphere tile, int fireCount)
@@ -124,7 +125,7 @@ namespace Content.Server.Atmos.EntitySystems
switch (tile.Air.LastShare)
{
case > Atmospherics.MinimumAirToSuspend:
tile.ExcitedGroup.ResetCooldowns();
ExcitedGroupResetCooldowns(tile.ExcitedGroup);
break;
case > Atmospherics.MinimumMolesDeltaToMove:
tile.ExcitedGroup.DismantleCooldown = 0;

View File

@@ -7,6 +7,7 @@ using Content.Server.Atmos.Components;
using Content.Server.Coordinates.Helpers;
using Content.Shared.Atmos;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
@@ -18,7 +19,7 @@ namespace Content.Server.Atmos.EntitySystems
private readonly TileAtmosphereComparer _monstermosComparer = new();
public void EqualizePressureInZone(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
public void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
{
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
return; // Already done.
@@ -80,7 +81,8 @@ namespace Content.Server.Atmos.EntitySystems
if (adj.Air.Immutable)
{
// Looks like someone opened an airlock to space!
ExplosivelyDepressurize(gridAtmosphere, tile, cycleNum);
ExplosivelyDepressurize(mapGrid, gridAtmosphere, tile, cycleNum);
return;
}
}
@@ -339,7 +341,7 @@ namespace Content.Server.Atmos.EntitySystems
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
var otherTile2 = otherTile.AdjacentTiles[j];
if (otherTile2?.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
gridAtmosphere.AddActiveTile(otherTile2);
AddActiveTile(gridAtmosphere, otherTile2);
break;
}
}
@@ -349,7 +351,7 @@ namespace Content.Server.Atmos.EntitySystems
ArrayPool<TileAtmosphere>.Shared.Return(takerTiles);
}
public void ExplosivelyDepressurize(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
public void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
{
// Check if explosive depressurization is enabled and if the tile is valid.
if (!MonstermosDepressurization || tile.Air == null)
@@ -389,7 +391,7 @@ namespace Content.Server.Atmos.EntitySystems
if (otherTile2.Air == null) continue;
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
ConsiderFirelocks(otherTile, otherTile2);
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
// The firelocks might have closed on us.
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
@@ -438,8 +440,8 @@ namespace Content.Server.Atmos.EntitySystems
{
var otherTile = progressionOrder[i];
if (otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
gridAtmosphere.AddHighPressureDelta(otherTile);
gridAtmosphere.AddActiveTile(otherTile);
gridAtmosphere.HighPressureDelta.Add(otherTile);
AddActiveTile(gridAtmosphere, otherTile);
var otherTile2 = otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()];
if (otherTile2?.Air == null) continue;
var sum = otherTile2.Air.TotalMoles;
@@ -455,9 +457,9 @@ namespace Content.Server.Atmos.EntitySystems
otherTile2.PressureDirection = otherTile.MonstermosInfo.CurrentTransferDirection;
}
otherTile.Air.Clear();
otherTile.UpdateVisuals();
HandleDecompressionFloorRip(gridAtmosphere, otherTile, sum);
otherTile.Air?.Clear();
InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices);
HandleDecompressionFloorRip(mapGrid, otherTile, sum);
}
ArrayPool<TileAtmosphere>.Shared.Return(tiles);
@@ -465,7 +467,7 @@ namespace Content.Server.Atmos.EntitySystems
ArrayPool<TileAtmosphere>.Shared.Return(progressionOrder);
}
private void ConsiderFirelocks(TileAtmosphere tile, TileAtmosphere other)
private void ConsiderFirelocks(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other)
{
if (!_mapManager.TryGetGrid(tile.GridIndex, out var mapGrid))
return;
@@ -491,8 +493,10 @@ namespace Content.Server.Atmos.EntitySystems
if (!reconsiderAdjacent)
return;
tile.UpdateAdjacent();
other.UpdateAdjacent();
UpdateAdjacent(mapGrid, gridAtmosphere, tile);
UpdateAdjacent(mapGrid, gridAtmosphere, other);
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
InvalidateVisuals(other.GridIndex, other.GridIndices);
}
public void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
@@ -524,8 +528,8 @@ namespace Content.Server.Atmos.EntitySystems
otherTile.MonstermosInfo[direction.GetOpposite()] = 0;
Merge(otherTile.Air, tile.Air.Remove(amount));
tile.UpdateVisuals();
otherTile.UpdateVisuals();
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices);
ConsiderPressureDifference(gridAtmosphere, tile, otherTile, amount);
}
}
@@ -548,12 +552,12 @@ namespace Content.Server.Atmos.EntitySystems
tile.AdjacentTiles[direction.ToIndex()].MonstermosInfo[direction.GetOpposite()] -= amount;
}
private void HandleDecompressionFloorRip(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, float sum)
private void HandleDecompressionFloorRip(IMapGrid mapGrid, TileAtmosphere tile, float sum)
{
var chance = MathHelper.Clamp(sum / 500, 0.005f, 0.5f);
if (sum > 20 && _robustRandom.Prob(chance))
gridAtmosphere.PryTile(tile.GridIndices);
PryTile(mapGrid, tile.GridIndices);
}
private class TileAtmosphereComparer : IComparer<TileAtmosphere>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.Piping.Components;
@@ -20,6 +21,11 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary>
private const int LagCheckIterations = 30;
/// <summary>
/// Check current execution time every n instances processed.
/// </summary>
private const int InvalidCoordinatesLagCheckIterations = 50;
private int _currentRunAtmosphereIndex = 0;
private bool _simulationPaused = false;
@@ -30,10 +36,13 @@ namespace Content.Server.Atmos.EntitySystems
if(!atmosphere.ProcessingPaused)
atmosphere.CurrentRunTiles = new Queue<TileAtmosphere>(atmosphere.ActiveTiles);
if (!TryGetMapGrid(atmosphere, out var mapGrid))
throw new Exception("Tried to process a grid atmosphere on an entity that isn't a grid!");
var number = 0;
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
{
EqualizePressureInZone(atmosphere, tile, atmosphere.UpdateCounter);
EqualizePressureInZone(mapGrid, atmosphere, tile, atmosphere.UpdateCounter);
if (number++ < LagCheckIterations) continue;
number = 0;
@@ -69,22 +78,22 @@ namespace Content.Server.Atmos.EntitySystems
return true;
}
private bool ProcessExcitedGroups(GridAtmosphereComponent atmosphere)
private bool ProcessExcitedGroups(GridAtmosphereComponent gridAtmosphere)
{
if(!atmosphere.ProcessingPaused)
atmosphere.CurrentRunExcitedGroups = new Queue<ExcitedGroup>(atmosphere.ExcitedGroups);
if(!gridAtmosphere.ProcessingPaused)
gridAtmosphere.CurrentRunExcitedGroups = new Queue<ExcitedGroup>(gridAtmosphere.ExcitedGroups);
var number = 0;
while (atmosphere.CurrentRunExcitedGroups.TryDequeue(out var excitedGroup))
while (gridAtmosphere.CurrentRunExcitedGroups.TryDequeue(out var excitedGroup))
{
excitedGroup.BreakdownCooldown++;
excitedGroup.DismantleCooldown++;
if(excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles)
excitedGroup.SelfBreakdown(this, ExcitedGroupsSpaceIsAllConsuming);
ExcitedGroupSelfBreakdown(gridAtmosphere, excitedGroup, ExcitedGroupsSpaceIsAllConsuming);
else if(excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
excitedGroup.Dismantle();
ExcitedGroupDismantle(gridAtmosphere, excitedGroup);
if (number++ < LagCheckIterations) continue;
number = 0;
@@ -195,7 +204,7 @@ namespace Content.Server.Atmos.EntitySystems
atmosphere.CurrentRunAtmosDevices = new Queue<AtmosDeviceComponent>(atmosphere.AtmosDevices);
var time = _gameTiming.CurTime;
var updateEvent = new AtmosDeviceUpdateEvent(atmosphere);
var updateEvent = new AtmosDeviceUpdateEvent();
var number = 0;
while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
{
@@ -237,8 +246,14 @@ namespace Content.Server.Atmos.EntitySystems
atmosphere.Timer += frameTime;
if (atmosphere.InvalidatedCoords.Count != 0)
atmosphere.Revalidate();
if ((atmosphere.InvalidatedCoords.Count != 0 || atmosphere.RevalidatePaused) && TryGetMapGrid(atmosphere, out var mapGrid))
if (!GridRevalidate(mapGrid, atmosphere))
{
atmosphere.RevalidatePaused = true;
return;
}
atmosphere.RevalidatePaused = false;
if (atmosphere.Timer < AtmosTime)
continue;

View File

@@ -48,21 +48,24 @@ namespace Content.Server.Atmos.EntitySystems
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
{
if (tile.ThermalConductivity == 0f)
if (tile.ThermalConductivity == 0f || !Superconduction)
return false;
gridAtmosphere.AddSuperconductivityTile(tile);
gridAtmosphere.SuperconductivityTiles.Add(tile);
return true;
}
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool starting)
{
if (!Superconduction)
return false;
if (tile.Air == null || tile.Air.Temperature < (starting
? Atmospherics.MinimumTemperatureStartSuperConduction
: Atmospherics.MinimumTemperatureForSuperconduction))
return false;
return !(gridAtmosphere.AtmosphereSystem.GetHeatCapacity(tile.Air) < Atmospherics.MCellWithRatio)
return !(GetHeatCapacity(tile.Air) < Atmospherics.MCellWithRatio)
&& ConsiderSuperconductivity(gridAtmosphere, tile);
}
@@ -82,7 +85,7 @@ namespace Content.Server.Atmos.EntitySystems
// Make sure it's still hot enough to continue conducting.
if (temperature < Atmospherics.MinimumTemperatureForSuperconduction)
{
gridAtmosphere.RemoveSuperconductivityTile(tile);
gridAtmosphere.SuperconductivityTiles.Remove(tile);
}
}
@@ -112,7 +115,7 @@ namespace Content.Server.Atmos.EntitySystems
TemperatureShareOpenToSolid(tile, other);
}
gridAtmosphere.AddActiveTile(tile);
AddActiveTile(gridAtmosphere, tile);
}
private void TemperatureShareOpenToSolid(TileAtmosphere tile, TileAtmosphere other)

View File

@@ -3,19 +3,15 @@ using Content.Server.NodeContainer.EntitySystems;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Maps;
using JetBrains.Annotations;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Server.Atmos.EntitySystems
{
[UsedImplicitly]
public partial class AtmosphereSystem : SharedAtmosphereSystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
private const float ExposedUpdateDelay = 1f;
private float _exposedTimer = 0f;
@@ -28,6 +24,7 @@ namespace Content.Server.Atmos.EntitySystems
InitializeGases();
InitializeCVars();
InitializeGrid();
#region Events
@@ -57,7 +54,7 @@ namespace Content.Server.Atmos.EntitySystems
return;
}
GetGridAtmosphere(eventArgs.NewTile.GridIndex)?.Invalidate(eventArgs.NewTile.GridIndices);
InvalidateTile(eventArgs.NewTile.GridIndex, eventArgs.NewTile.GridIndices);
}
private void OnMapCreated(object? sender, MapEventArgs e)
@@ -84,8 +81,7 @@ namespace Content.Server.Atmos.EntitySystems
foreach (var exposed in EntityManager.ComponentManager.EntityQuery<AtmosExposedComponent>())
{
// TODO ATMOS: Kill this with fire.
var atmos = GetGridAtmosphere(exposed.Owner.Transform.Coordinates);
var tile = atmos.GetTile(exposed.Owner.Transform.Coordinates);
var tile = GetTileAtmosphereOrCreateSpace(exposed.Owner.Transform.Coordinates);
if (tile == null) continue;
exposed.Update(tile, _exposedTimer, this);
}

View File

@@ -144,14 +144,14 @@ namespace Content.Server.Atmos.EntitySystems
/// <summary>
/// Checks whether the overlay-relevant data for a gas tile has been updated.
/// </summary>
/// <param name="gam"></param>
/// <param name="grid"></param>
/// <param name="oldTile"></param>
/// <param name="indices"></param>
/// <param name="overlayData"></param>
/// <returns>true if updated</returns>
private bool TryRefreshTile(GridAtmosphereComponent gam, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData)
private bool TryRefreshTile(GridId grid, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData)
{
var tile = gam.GetTile(indices);
var tile = _atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, indices);
if (tile == null)
{
@@ -287,7 +287,7 @@ namespace Content.Server.Atmos.EntitySystems
{
var chunk = GetOrCreateChunk(gridId, invalid);
if (!TryRefreshTile(gam, chunk.GetData(invalid), invalid, out var data)) continue;
if (!TryRefreshTile(grid.Index, chunk.GetData(invalid), invalid, out var data)) continue;
if (!updatedTiles.TryGetValue(chunk, out var tiles))
{

View File

@@ -1,146 +1,16 @@
using System;
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using System.Collections.Generic;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos
{
public class ExcitedGroup : IDisposable
public class ExcitedGroup
{
[ViewVariables]
private bool _disposed = false;
[ViewVariables] public bool Disposed = false;
[ViewVariables]
private readonly HashSet<TileAtmosphere> _tiles = new();
[ViewVariables] public readonly List<TileAtmosphere> Tiles = new(100);
[ViewVariables]
private GridAtmosphereComponent _gridAtmosphereComponent = default!;
[ViewVariables] public int DismantleCooldown { get; set; } = 0;
[ViewVariables]
public int DismantleCooldown { get; set; }
[ViewVariables]
public int BreakdownCooldown { get; set; }
public void AddTile(TileAtmosphere tile)
{
_tiles.Add(tile);
tile.ExcitedGroup = this;
ResetCooldowns();
}
public bool RemoveTile(TileAtmosphere tile)
{
tile.ExcitedGroup = null;
return _tiles.Remove(tile);
}
public void MergeGroups(ExcitedGroup other)
{
var ourSize = _tiles.Count;
var otherSize = other._tiles.Count;
if (ourSize > otherSize)
{
foreach (var tile in other._tiles)
{
tile.ExcitedGroup = this;
_tiles.Add(tile);
}
other._tiles.Clear();
other.Dispose();
ResetCooldowns();
}
else
{
foreach (var tile in _tiles)
{
tile.ExcitedGroup = other;
other._tiles.Add(tile);
}
_tiles.Clear();
Dispose();
other.ResetCooldowns();
}
}
~ExcitedGroup()
{
Dispose();
}
public void Initialize(GridAtmosphereComponent gridAtmosphereComponent)
{
_gridAtmosphereComponent = gridAtmosphereComponent;
_gridAtmosphereComponent.AddExcitedGroup(this);
}
public void ResetCooldowns()
{
BreakdownCooldown = 0;
DismantleCooldown = 0;
}
public void SelfBreakdown(AtmosphereSystem atmosphereSystem, bool spaceIsAllConsuming = false)
{
var combined = new GasMixture(Atmospherics.CellVolume);
var tileSize = _tiles.Count;
if (_disposed) return;
if (tileSize == 0)
{
Dispose();
return;
}
foreach (var tile in _tiles)
{
if (tile?.Air == null) continue;
atmosphereSystem.Merge(combined, tile.Air);
if (!spaceIsAllConsuming || !tile.Air.Immutable) continue;
combined.Clear();
break;
}
combined.Multiply(1 / (float)tileSize);
foreach (var tile in _tiles)
{
if (tile?.Air == null) continue;
tile.Air.CopyFromMutable(combined);
tile.UpdateVisuals();
}
BreakdownCooldown = 0;
}
public void Dismantle(bool unexcite = true)
{
foreach (var tile in _tiles)
{
if (tile == null) continue;
tile.ExcitedGroup = null;
if (!unexcite) continue;
tile.Excited = false;
_gridAtmosphereComponent.RemoveActiveTile(tile);
}
_tiles.Clear();
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_gridAtmosphereComponent.RemoveExcitedGroup(this);
Dismantle(false);
_gridAtmosphereComponent = null!;
}
[ViewVariables] public int BreakdownCooldown { get; set; } = 0;
}
}

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.Atmos.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -21,40 +22,18 @@ namespace Content.Server.Atmos.Piping.Components
[DataField("requireAnchored")]
public bool RequireAnchored { get; private set; } = true;
public IGridAtmosphereComponent? Atmosphere { get; set; }
[ViewVariables]
public TimeSpan LastProcess { get; set; } = TimeSpan.Zero;
public GridId? JoinedGrid { get; set; }
}
public abstract class BaseAtmosDeviceEvent : EntityEventArgs
{
public IGridAtmosphereComponent Atmosphere { get; }
public sealed class AtmosDeviceUpdateEvent : EntityEventArgs
{}
public BaseAtmosDeviceEvent(IGridAtmosphereComponent atmosphere)
{
Atmosphere = atmosphere;
}
}
public sealed class AtmosDeviceEnabledEvent : EntityEventArgs
{}
public sealed class AtmosDeviceUpdateEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceUpdateEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
public sealed class AtmosDeviceEnabledEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceEnabledEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
public sealed class AtmosDeviceDisabledEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceDisabledEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
public sealed class AtmosDeviceDisabledEvent : EntityEventArgs
{}
}

View File

@@ -20,13 +20,13 @@ namespace Content.Server.Atmos.Piping.EntitySystems
SubscribeLocalEvent<AtmosDeviceComponent, ComponentInit>(OnDeviceInitialize);
SubscribeLocalEvent<AtmosDeviceComponent, ComponentShutdown>(OnDeviceShutdown);
SubscribeLocalEvent<AtmosDeviceComponent, PhysicsBodyTypeChangedEvent>(OnDeviceBodyTypeChanged);
SubscribeLocalEvent<AtmosDeviceComponent, EntParentChangedMessage>(OnDeviceParentChanged);
SubscribeLocalEvent<AtmosDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
}
private bool CanJoinAtmosphere(AtmosDeviceComponent component)
{
return !component.RequireAnchored || !component.Owner.TryGetComponent(out PhysicsComponent? physics) || physics.BodyType == BodyType.Static;
return !component.RequireAnchored || !component.Owner.Transform.Anchored;
}
public void JoinAtmosphere(AtmosDeviceComponent component)
@@ -34,26 +34,22 @@ namespace Content.Server.Atmos.Piping.EntitySystems
if (!CanJoinAtmosphere(component))
return;
// We try to get a valid, simulated atmosphere.
if (!Get<AtmosphereSystem>().TryGetSimulatedGridAtmosphere(component.Owner.Transform.MapPosition, out var atmosphere))
// We try to add the device to a valid atmosphere.
if (!Get<AtmosphereSystem>().AddAtmosDevice(component))
return;
component.LastProcess = _gameTiming.CurTime;
component.Atmosphere = atmosphere;
atmosphere.AddAtmosDevice(component);
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceEnabledEvent(atmosphere), false);
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceEnabledEvent(), false);
}
public void LeaveAtmosphere(AtmosDeviceComponent component)
{
var atmosphere = component.Atmosphere;
atmosphere?.RemoveAtmosDevice(component);
component.Atmosphere = null;
component.LastProcess = TimeSpan.Zero;
if (!Get<AtmosphereSystem>().RemoveAtmosDevice(component))
return;
if(atmosphere != null)
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(atmosphere), false);
component.LastProcess = TimeSpan.Zero;
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(), false);
}
public void RejoinAtmosphere(AtmosDeviceComponent component)
@@ -72,13 +68,13 @@ namespace Content.Server.Atmos.Piping.EntitySystems
LeaveAtmosphere(component);
}
private void OnDeviceBodyTypeChanged(EntityUid uid, AtmosDeviceComponent component, PhysicsBodyTypeChangedEvent args)
private void OnDeviceAnchorChanged(EntityUid uid, AtmosDeviceComponent component, AnchorStateChangedEvent args)
{
// Do nothing if the component doesn't require being anchored to function.
if (!component.RequireAnchored)
return;
if (args.New == BodyType.Static)
if(component.Owner.Transform.Anchored)
JoinAtmosphere(component);
else
LeaveAtmosphere(component);

View File

@@ -21,9 +21,8 @@ namespace Content.Server.Atmos.Reactions
var location = holder as TileAtmosphere;
mixture.ReactionResults[GasReaction.Fire] = 0;
// More plasma released at higher temperatures
// More plasma released at higher temperatures.
var temperatureScale = 0f;
var superSaturation = false;
if (temperature > Atmospherics.PlasmaUpperTemperature)
temperatureScale = 1f;
@@ -31,33 +30,31 @@ namespace Content.Server.Atmos.Reactions
temperatureScale = (temperature - Atmospherics.PlasmaMinimumBurnTemperature) /
(Atmospherics.PlasmaUpperTemperature - Atmospherics.PlasmaMinimumBurnTemperature);
if (temperatureScale > 0f)
if (temperatureScale > 0)
{
var plasmaBurnRate = 0f;
var oxygenBurnRate = Atmospherics.OxygenBurnRateBase - temperatureScale;
var plasmaBurnRate = 0f;
if (mixture.GetMoles(Gas.Oxygen) / mixture.GetMoles(Gas.Plasma) >
Atmospherics.SuperSaturationThreshold)
superSaturation = true;
var initialOxygenMoles = mixture.GetMoles(Gas.Oxygen);
var initialPlasmaMoles = mixture.GetMoles(Gas.Plasma);
if (mixture.GetMoles(Gas.Oxygen) >
mixture.GetMoles(Gas.Plasma) * Atmospherics.PlasmaOxygenFullburn)
plasmaBurnRate = (mixture.GetMoles(Gas.Plasma) * temperatureScale) /
Atmospherics.PlasmaBurnRateDelta;
// Supersaturation makes tritium.
var supersaturation = initialOxygenMoles / initialPlasmaMoles > Atmospherics.SuperSaturationThreshold;
if (initialOxygenMoles > initialPlasmaMoles * Atmospherics.PlasmaOxygenFullburn)
plasmaBurnRate = initialPlasmaMoles * temperatureScale / Atmospherics.PlasmaBurnRateDelta;
else
plasmaBurnRate = (temperatureScale * (mixture.GetMoles(Gas.Oxygen) / Atmospherics.PlasmaOxygenFullburn)) / Atmospherics.PlasmaBurnRateDelta;
plasmaBurnRate = temperatureScale * (initialOxygenMoles / Atmospherics.PlasmaOxygenFullburn) / Atmospherics.PlasmaBurnRateDelta;
if (plasmaBurnRate > Atmospherics.MinimumHeatCapacity)
{
plasmaBurnRate = MathF.Min(MathF.Min(plasmaBurnRate, mixture.GetMoles(Gas.Plasma)), mixture.GetMoles(Gas.Oxygen)/oxygenBurnRate);
mixture.SetMoles(Gas.Plasma, mixture.GetMoles(Gas.Plasma) - plasmaBurnRate);
mixture.SetMoles(Gas.Oxygen, mixture.GetMoles(Gas.Oxygen) - (plasmaBurnRate * oxygenBurnRate));
plasmaBurnRate = MathF.Min(plasmaBurnRate, MathF.Min(initialPlasmaMoles, initialOxygenMoles / oxygenBurnRate));
mixture.SetMoles(Gas.Plasma, initialPlasmaMoles - plasmaBurnRate);
mixture.SetMoles(Gas.Oxygen, initialOxygenMoles - plasmaBurnRate * oxygenBurnRate);
mixture.AdjustMoles(supersaturation ? Gas.Tritium : Gas.CarbonDioxide, plasmaBurnRate);
mixture.AdjustMoles(superSaturation ? Gas.Tritium : Gas.CarbonDioxide, plasmaBurnRate);
energyReleased += Atmospherics.FirePlasmaEnergyReleased * (plasmaBurnRate);
mixture.ReactionResults[GasReaction.Fire] += (plasmaBurnRate) * (1 + oxygenBurnRate);
energyReleased += Atmospherics.FirePlasmaEnergyReleased * plasmaBurnRate;
mixture.ReactionResults[GasReaction.Fire] += plasmaBurnRate * (1 + oxygenBurnRate);
}
}
@@ -65,25 +62,25 @@ namespace Content.Server.Atmos.Reactions
{
var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture);
if (newHeatCapacity > Atmospherics.MinimumHeatCapacity)
mixture.Temperature = ((temperature * oldHeatCapacity + energyReleased) / newHeatCapacity);
mixture.Temperature = (temperature * oldHeatCapacity + energyReleased) / newHeatCapacity;
}
if (location != null)
{
temperature = mixture.Temperature;
if (temperature > Atmospherics.FireMinimumTemperatureToExist)
var mixTemperature = mixture.Temperature;
if (mixTemperature > Atmospherics.FireMinimumTemperatureToExist)
{
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume);
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, mixTemperature, mixture.Volume);
foreach (var entity in location.GridIndices.GetEntitiesInTileFast(location.GridIndex))
{
foreach (var temperatureExpose in entity.GetAllComponents<ITemperatureExpose>())
{
temperatureExpose.TemperatureExpose(mixture, temperature, mixture.Volume);
temperatureExpose.TemperatureExpose(mixture, mixTemperature, mixture.Volume);
}
}
// TODO ATMOS: location.TemperatureExpose(mixture, temperature, mixture.Volume);
// TODO ATMOS: location.TemperatureExpose(mixture, mixTemperature, mixture.Volume);
}
}

View File

@@ -1,7 +1,5 @@
#nullable disable warnings
#nullable enable annotations
using System.Runtime.CompilerServices;
using Content.Server.Atmos.Components;
using Content.Server.Interfaces;
using Content.Shared.Atmos;
using Content.Shared.Maps;
@@ -11,6 +9,9 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos
{
/// <summary>
/// Internal Atmos class that stores data about the atmosphere in a grid.
/// </summary>
public class TileAtmosphere : IGasMixtureHolder
{
[ViewVariables]
@@ -40,9 +41,6 @@ namespace Content.Server.Atmos
[ViewVariables]
public bool Excited { get; set; }
[ViewVariables]
private readonly GridAtmosphereComponent _gridAtmosphereComponent;
/// <summary>
/// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW)
/// </summary>
@@ -86,9 +84,8 @@ namespace Content.Server.Atmos
[ViewVariables]
public AtmosDirection BlockedAirflow { get; set; } = AtmosDirection.Invalid;
public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false)
public TileAtmosphere(GridId gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false)
{
_gridAtmosphereComponent = atmosphereComponent;
GridIndex = gridIndex;
GridIndices = gridIndices;
Air = mixture;
@@ -96,47 +93,5 @@ namespace Content.Server.Atmos
if(immutable)
Air?.MarkImmutable();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UpdateVisuals()
{
if (Air == null) return;
_gridAtmosphereComponent.GasTileOverlaySystem.Invalidate(GridIndex, GridIndices);
}
public void UpdateAdjacent()
{
for (var i = 0; i < Atmospherics.Directions; i++)
{
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 && !BlockedAirflow.IsFlagSet(direction) && !_gridAtmosphereComponent.IsAirBlocked(adjacent.GridIndices, direction.GetOpposite()))
{
AdjacentBits |= direction;
}
}
}
private void UpdateAdjacent(AtmosDirection direction)
{
AdjacentTiles[direction.ToIndex()] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction.ToDirection()));
if (!BlockedAirflow.IsFlagSet(direction) && !_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection()), direction.GetOpposite()))
{
AdjacentBits |= direction;
}
else
{
AdjacentBits &= ~direction;
}
}
}
}

View File

@@ -8,7 +8,9 @@ using Content.Server.Interfaces;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.NodeContainer.NodeGroups
@@ -30,16 +32,14 @@ namespace Content.Server.NodeContainer.NodeGroups
[ViewVariables] private AtmosphereSystem? _atmosphereSystem;
[ViewVariables]
private IGridAtmosphereComponent? GridAtmos =>
_atmosphereSystem?.GetGridAtmosphere(GridId);
public GridId Grid => GridId;
public override void Initialize(Node sourceNode)
{
base.Initialize(sourceNode);
_atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
GridAtmos?.AddPipeNet(this);
_atmosphereSystem.AddPipeNet(this);
}
public void Update()
@@ -94,7 +94,8 @@ namespace Content.Server.NodeContainer.NodeGroups
private void RemoveFromGridAtmos()
{
GridAtmos?.RemovePipeNet(this);
DebugTools.AssertNotNull(_atmosphereSystem);
_atmosphereSystem?.RemovePipeNet(this);
}
}
}