diff --git a/Content.Benchmarks/Content.Benchmarks.csproj b/Content.Benchmarks/Content.Benchmarks.csproj index 194ddc4d61..ba1b30955e 100644 --- a/Content.Benchmarks/Content.Benchmarks.csproj +++ b/Content.Benchmarks/Content.Benchmarks.csproj @@ -23,6 +23,5 @@ - diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index e9dca425ed..96e8afa4c5 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -9,6 +9,7 @@ using Content.Server.Interfaces; using Content.Shared.Atmos; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -24,11 +25,12 @@ namespace Content.Server.Atmos public static GasMixture SpaceGas => new GasMixture() {Volume = 2500f, Immutable = true, Temperature = Atmospherics.TCMB}; + // This must always have a length that is a multiple of 4 for SIMD acceleration. [ViewVariables] - private float[] _moles = new float[Atmospherics.TotalNumberOfGases]; + private float[] _moles; [ViewVariables] - private float[] _molesArchived = new float[Atmospherics.TotalNumberOfGases]; + private float[] _molesArchived; [ViewVariables] private float _temperature = Atmospherics.TCMB; @@ -54,46 +56,10 @@ namespace Content.Server.Atmos [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - var capacity = 0f; + Span tmp = stackalloc float[_moles.Length]; + NumericsHelpers.Multiply(_moles, _atmosphereSystem.GasSpecificHeats, tmp); - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - capacity += _atmosphereSystem.GetGas(i).SpecificHeat * _moles[i]; - } - - return MathF.Max(capacity, Atmospherics.MinimumHeatCapacity); - } - } - - /// - /// Heat capacity ratio of gas mixture - /// - [ViewVariables] - public float HeatCapacityRatio - { - get - { - var delimiterSum = 0f; - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - delimiterSum += _moles[i] / (_atmosphereSystem.GetGas(i).HeatCapacityRatio - 1); - } - return 1 + TotalMoles / delimiterSum; - } - } - - public float MolarMass - { - get - { - var molarMass = 0f; - var totalMoles = TotalMoles; - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - molarMass += _atmosphereSystem.GetGas(i).MolarMass * (_moles[i] / totalMoles); - } - - return molarMass; + return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); } } @@ -103,14 +69,10 @@ namespace Content.Server.Atmos [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - var capacity = 0f; + Span tmp = stackalloc float[_moles.Length]; + NumericsHelpers.Multiply(_molesArchived, _atmosphereSystem.GasSpecificHeats, tmp); - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - capacity += _atmosphereSystem.GetGas(i).SpecificHeat * _molesArchived[i]; - } - - return MathF.Max(capacity, Atmospherics.MinimumHeatCapacity); + return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); } } @@ -118,17 +80,7 @@ namespace Content.Server.Atmos public float TotalMoles { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - var moles = 0f; - - foreach (var gas in _moles) - { - moles += gas; - } - - return moles; - } + get => NumericsHelpers.HorizontalAdd(_moles); } [ViewVariables] @@ -168,7 +120,7 @@ namespace Content.Server.Atmos public GasMixture(AtmosphereSystem? atmosphereSystem) { _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get(); - _moles = new float[_atmosphereSystem.Gases.Count()]; + _moles = new float[MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)]; _molesArchived = new float[_moles.Length]; } @@ -207,10 +159,7 @@ namespace Content.Server.Atmos } } - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - _moles[i] += giver._moles[i]; - } + NumericsHelpers.Add(_moles, giver._moles); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -268,19 +217,10 @@ namespace Content.Server.Atmos var removed = new GasMixture(_atmosphereSystem) {Volume = Volume, Temperature = Temperature}; - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - var moles = _moles[i]; - if (moles < Atmospherics.GasMinMoles) - removed._moles[i] = 0f; - else - { - var removedMoles = moles * ratio; - removed._moles[i] = removedMoles; - if (!Immutable) - _moles[i] -= removedMoles; - } - } + _moles.CopyTo(removed._moles.AsSpan()); + NumericsHelpers.Multiply(removed._moles, ratio); + if (!Immutable) + NumericsHelpers.Sub(_moles, removed._moles); return removed; } @@ -563,25 +503,24 @@ namespace Content.Server.Atmos public void Multiply(float multiplier) { if (Immutable) return; - for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - _moles[i] *= multiplier; - } + NumericsHelpers.Multiply(_moles, multiplier); } public void ExposeData(ObjectSerializer serializer) { + var length = MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4); + serializer.DataField(this, x => Immutable, "immutable", false); serializer.DataField(this, x => Volume, "volume", 0f); serializer.DataField(this, x => LastShare, "lastShare", 0f); serializer.DataField(this, x => TemperatureArchived, "temperatureArchived", 0f); - serializer.DataField(ref _moles, "moles", new float[Atmospherics.TotalNumberOfGases]); - serializer.DataField(ref _molesArchived, "molesArchived", new float[Atmospherics.TotalNumberOfGases]); + serializer.DataField(ref _moles, "moles", new float[length]); + serializer.DataField(ref _molesArchived, "molesArchived", new float[length]); serializer.DataField(ref _temperature, "temperature", Atmospherics.TCMB); // The arrays MUST have a specific length. - Array.Resize(ref _moles, Atmospherics.TotalNumberOfGases); - Array.Resize(ref _molesArchived, Atmospherics.TotalNumberOfGases); + Array.Resize(ref _moles, length); + Array.Resize(ref _molesArchived, length); } public override bool Equals(object? obj) diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index eaebbd2f7e..2ecb268be0 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -252,7 +252,7 @@ namespace Content.Server.Atmos for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if (!_adjacentBits.HasFlag(direction)) continue; + if (!_adjacentBits.IsFlagSet(direction)) continue; var other = _adjacentTiles[i]; if (other?.Air == null) continue; var comparisonMoles = other.Air.TotalMoles; @@ -288,7 +288,7 @@ namespace Content.Server.Atmos for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - if (!exploring._adjacentBits.HasFlag(direction)) continue; + if (!exploring._adjacentBits.IsFlagSet(direction)) continue; var adj = exploring._adjacentTiles[j]; if (adj?.Air == null) continue; if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue; @@ -360,7 +360,7 @@ namespace Content.Server.Atmos for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(direction)) continue; var tile2 = tile._adjacentTiles[j]; // skip anything that isn't part of our current processing block. @@ -378,7 +378,7 @@ namespace Content.Server.Atmos for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - if (!eligibleDirections.HasFlag(direction)) continue; + if (!eligibleDirections.IsFlagSet(direction)) continue; tile.AdjustEqMovement(direction, molesToMove); tile._tileAtmosInfo.MoleDelta -= molesToMove; @@ -425,7 +425,7 @@ namespace Content.Server.Atmos for (var k = 0; k < Atmospherics.Directions; k++) { var direction = (AtmosDirection) (1 << k); - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(direction)) continue; var tile2 = tile._adjacentTiles[k]; if (giver._tileAtmosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed. if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue; @@ -493,7 +493,7 @@ namespace Content.Server.Atmos for (var k = 0; k < Atmospherics.Directions; k++) { var direction = (AtmosDirection) (1 << k); - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(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. @@ -554,7 +554,7 @@ namespace Content.Server.Atmos for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(direction)) continue; var tile2 = tile._adjacentTiles[j]; if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange) continue; _gridAtmosphereComponent.AddActiveTile(tile2); @@ -587,7 +587,7 @@ namespace Content.Server.Atmos for(var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if (!_adjacentBits.HasFlag(direction)) continue; + if (!_adjacentBits.IsFlagSet(direction)) continue; var amount = transferDirections[i]; var tile = _adjacentTiles[i]; if (tile?.Air == null) continue; @@ -612,7 +612,7 @@ namespace Content.Server.Atmos { var direction = (AtmosDirection) (1 << i); var amount = transferDirs[i]; - if(amount < 0 && _adjacentBits.HasFlag(direction)) + if(amount < 0 && _adjacentBits.IsFlagSet(direction)) _adjacentTiles[i].FinalizeEq(); // A bit of recursion if needed. } } @@ -654,14 +654,14 @@ namespace Content.Server.Atmos for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if(_adjacentBits.HasFlag(direction)) + if(_adjacentBits.IsFlagSet(direction)) adjacentTileLength++; } for(var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if (!_adjacentBits.HasFlag(direction)) continue; + if (!_adjacentBits.IsFlagSet(direction)) continue; var enemyTile = _adjacentTiles[i]; // If the tile is null or has no air, we don't do anything for it. @@ -848,7 +848,7 @@ namespace Content.Server.Atmos for(var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if (!directions.HasFlag(direction)) continue; + if (!directions.IsFlagSet(direction)) continue; var adjacent = _adjacentTiles[direction.ToIndex()]; @@ -1002,7 +1002,7 @@ namespace Content.Server.Atmos for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(direction)) continue; var tile2 = tile._adjacentTiles[j]; if (tile2.Air == null) continue; if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue; @@ -1010,7 +1010,7 @@ namespace Content.Server.Atmos tile.ConsiderFirelocks(tile2); // The firelocks might have closed on us. - if (!tile._adjacentBits.HasFlag(direction)) continue; + if (!tile._adjacentBits.IsFlagSet(direction)) continue; tile2._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle}; tiles[tileCount++] = tile2; } @@ -1039,7 +1039,7 @@ namespace Content.Server.Atmos { 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; + if (!tile._adjacentBits.IsFlagSet(direction) && !tile.Air.Immutable) continue; var tile2 = tile._adjacentTiles[j]; if (tile2?._tileAtmosInfo.LastQueueCycle != queueCycle) continue; if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue; @@ -1156,7 +1156,7 @@ namespace Content.Server.Atmos _adjacentTiles[direction.ToIndex()] = adjacent; adjacent?.UpdateAdjacent(direction.GetOpposite()); - if (adjacent != null && !BlockedAirflow.HasFlag(direction) && !_gridAtmosphereComponent.IsAirBlocked(adjacent.GridIndices, direction.GetOpposite())) + if (adjacent != null && !BlockedAirflow.IsFlagSet(direction) && !_gridAtmosphereComponent.IsAirBlocked(adjacent.GridIndices, direction.GetOpposite())) { _adjacentBits |= direction; } @@ -1167,7 +1167,7 @@ namespace Content.Server.Atmos { _adjacentTiles[direction.ToIndex()] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction.ToDirection())); - if (!BlockedAirflow.HasFlag(direction) && !_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection()), direction.GetOpposite())) + if (!BlockedAirflow.IsFlagSet(direction) && !_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection()), direction.GetOpposite())) { _adjacentBits |= direction; } diff --git a/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs b/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs index e2c4357d58..03f9c7f8cc 100644 --- a/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs @@ -113,7 +113,7 @@ namespace Content.Server.GameObjects.Components.Atmos for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); - if (!myDirection.HasFlag(direction)) continue; + if (!myDirection.IsFlagSet(direction)) continue; var angle = direction.ToAngle(); angle += myAngle; newAirBlockedDirs |= angle.ToAtmosDirectionCardinal(); diff --git a/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs index 14f9b1a9a2..99bb553b3d 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs @@ -233,6 +233,7 @@ namespace Content.Server.GameObjects.Components.Atmos private InternalsComponent? GetInternalsComponent(IEntity? owner = null) { + if (Owner.Deleted) return null; if (owner != null) return owner.GetComponentOrNull(); return Owner.TryGetContainer(out var container) ? container.Owner.GetComponentOrNull() diff --git a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs index df501e9bee..e07f6b6b08 100644 --- a/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs @@ -14,6 +14,7 @@ using Content.Shared.Maps; using Robust.Server.GameObjects.EntitySystems.TileLookup; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.ComponentDependencies; using Robust.Shared.GameObjects.Components.Map; using Robust.Shared.GameObjects.Components.Transform; using Robust.Shared.GameObjects.Systems; @@ -65,6 +66,8 @@ namespace Content.Server.GameObjects.Components.Atmos private Stopwatch _stopwatch = new Stopwatch(); private GridId _gridId; + [ComponentDependency] private IMapGridComponent? _mapGridComponent = default!; + [ViewVariables] public int UpdateCounter { get; private set; } = 0; @@ -217,7 +220,7 @@ namespace Content.Server.GameObjects.Components.Atmos protected virtual void Revalidate() { - foreach (var indices in _invalidatedCoords.ToArray()) + foreach (var indices in _invalidatedCoords) { var tile = GetTile(indices); @@ -259,7 +262,7 @@ namespace Content.Server.GameObjects.Components.Atmos // 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(); - tile.UpdateVisuals(); + GasTileOverlaySystem.Invalidate(_gridId, indices); for (var i = 0; i < Atmospherics.Directions; i++) { @@ -420,12 +423,18 @@ namespace Content.Server.GameObjects.Components.Atmos /// public bool IsAirBlocked(Vector2i indices, AtmosDirection direction = AtmosDirection.All) { + var directions = AtmosDirection.Invalid; + foreach (var obstructingComponent in GetObstructingComponents(indices)) { if (!obstructingComponent.AirBlocked) continue; - if (obstructingComponent.AirBlockedDirection.HasFlag(direction)) + // 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; } @@ -435,10 +444,9 @@ namespace Content.Server.GameObjects.Components.Atmos /// public virtual bool IsSpace(Vector2i indices) { - // TODO ATMOS use ContentTileDefinition to define in YAML whether or not a tile is considered space - if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default; + if (_mapGridComponent == null) return default; - return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty; + return _mapGridComponent.Grid.GetTileRef(indices).IsSpace(); } public Dictionary GetAdjacentTiles(Vector2i indices, bool includeAirBlocked = false) @@ -461,9 +469,9 @@ namespace Content.Server.GameObjects.Components.Atmos /// public float GetVolumeForCells(int cellCount) { - if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default; + if (_mapGridComponent == null) return default; - return mapGrid.Grid.TileSize * cellCount * Atmospherics.CellVolume; + return _mapGridComponent.Grid.TileSize * cellCount * Atmospherics.CellVolume; } /// @@ -797,15 +805,11 @@ namespace Content.Server.GameObjects.Components.Atmos { var gridLookup = EntitySystem.Get(); - var list = new List(); - foreach (var v in gridLookup.GetEntitiesIntersecting(_gridId, indices)) { if (v.TryGetComponent(out var ac)) - list.Add(ac); + yield return ac; } - - return list; } private bool NeedsVacuumFixing(Vector2i indices) @@ -841,8 +845,7 @@ namespace Content.Server.GameObjects.Components.Atmos public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - if (serializer.Reading && - Owner.TryGetComponent(out IMapGridComponent? mapGrid)) + if (serializer.Reading && Owner.TryGetComponent(out IMapGridComponent? mapGrid)) { var gridId = mapGrid.Grid.Index; diff --git a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs index 5f395d2c36..fdaf7ca0c0 100644 --- a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs @@ -243,6 +243,7 @@ namespace Content.Server.GameObjects.Components.Medical public void EjectBody() { var containedEntity = _bodyContainer.ContainedEntity; + if (containedEntity == null) return; _bodyContainer.Remove(containedEntity); containedEntity.Transform.WorldPosition += _ejectOffset; UpdateUserInterface(); diff --git a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs index 14074c22b4..3ebcd1356a 100644 --- a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs @@ -156,21 +156,22 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos var tileData = new List(); - for (byte i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - var gas = _atmosphereSystem.GetGas(i); - var overlay = _atmosphereSystem.GetOverlay(i); - if (overlay == null || tile?.Air == null) continue; + if(tile.Air != null) + for (byte i = 0; i < Atmospherics.TotalNumberOfGases; i++) + { + var gas = _atmosphereSystem.GetGas(i); + var overlay = _atmosphereSystem.GetOverlay(i); + if (overlay == null) continue; - var moles = tile.Air.Gases[i]; + var moles = tile.Air.Gases[i]; - if (moles < gas.GasMolesVisible) continue; + if (moles < gas.GasMolesVisible) continue; - var data = new GasData(i, (byte) (MathHelper.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); - tileData.Add(data); - } + var data = new GasData(i, (byte) (MathHelper.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); + tileData.Add(data); + } - overlayData = new GasOverlayData(tile!.Hotspot.State, tile.Hotspot.Temperature, tileData.Count == 0 ? null : tileData.ToArray()); + overlayData = new GasOverlayData(tile!.Hotspot.State, tile.Hotspot.Temperature, tileData.Count == 0 ? Array.Empty() : tileData.ToArray()); if (overlayData.Equals(oldTile)) { diff --git a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs index 628b81dbae..cd1919ba4c 100644 --- a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs @@ -5,7 +5,9 @@ using System.Linq; using Content.Server.Atmos; using Content.Server.Atmos.Reactions; using Content.Server.GameObjects.Components.Atmos; +using Content.Shared.Atmos; using Content.Shared.GameObjects.EntitySystems.Atmos; +using Content.Shared.Maps; using JetBrains.Annotations; using Robust.Server.GameObjects.EntitySystems.TileLookup; using Robust.Server.Interfaces.Timing; @@ -17,6 +19,7 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; using Robust.Shared.Map; +using Robust.Shared.Maths; using Robust.Shared.Prototypes; namespace Content.Server.GameObjects.EntitySystems @@ -38,6 +41,9 @@ namespace Content.Server.GameObjects.EntitySystems /// public IEnumerable GasReactions => _gasReactions!; + private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases]; + public float[] GasSpecificHeats => _gasSpecificHeats; + public GridTileLookupSystem GridTileLookupSystem => _gridTileLookup ??= Get(); public override void Initialize() @@ -53,6 +59,13 @@ namespace Content.Server.GameObjects.EntitySystems _mapManager.TileChanged += OnTileChanged; + Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); + + for (var i = 0; i < GasPrototypes.Length; i++) + { + _gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat; + } + // Required for airtight components. EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, RotateEvent); } @@ -104,7 +117,7 @@ namespace Content.Server.GameObjects.EntitySystems // space -> not space or vice versa. So if the old tile is the // same as the new tile in terms of space-ness, ignore the change - if (eventArgs.NewTile.Tile.IsEmpty == eventArgs.OldTile.IsEmpty) + if (eventArgs.NewTile.IsSpace() == eventArgs.OldTile.IsSpace()) { return; } diff --git a/Content.Shared/Atmos/AtmosDirection.cs b/Content.Shared/Atmos/AtmosDirection.cs index df21a6d875..4922500f25 100644 --- a/Content.Shared/Atmos/AtmosDirection.cs +++ b/Content.Shared/Atmos/AtmosDirection.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Robust.Shared.Maths; using Robust.Shared.Serialization; @@ -135,6 +136,12 @@ namespace Content.Shared.Atmos { return direction & ~other; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFlagSet(this AtmosDirection direction, AtmosDirection other) + { + return (direction & other) != 0; + } } public sealed class AtmosDirectionFlags { } diff --git a/Content.Shared/Construction/ConstructionConditions/TileType.cs b/Content.Shared/Construction/ConstructionConditions/TileType.cs index e7e16240b5..cbac83577b 100644 --- a/Content.Shared/Construction/ConstructionConditions/TileType.cs +++ b/Content.Shared/Construction/ConstructionConditions/TileType.cs @@ -27,7 +27,7 @@ namespace Content.Shared.Construction.ConstructionConditions if (tileFound == null) return false; - var tile = TurfHelpers.GetContentTileDefinition(tileFound.Value.Tile); + var tile = tileFound.Value.Tile.GetContentTileDefinition(); foreach (var targetTile in TargetTiles) { if (tile.Name == targetTile) { diff --git a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosphereSystem.cs b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosphereSystem.cs index 541a30d859..2739e006e7 100644 --- a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosphereSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedAtmosphereSystem.cs @@ -11,9 +11,9 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases]; + protected readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases]; - private readonly SpriteSpecifier[] GasOverlays = new SpriteSpecifier[Atmospherics.TotalNumberOfGases]; + private readonly SpriteSpecifier[] _gasOverlays = new SpriteSpecifier[Atmospherics.TotalNumberOfGases]; public override void Initialize() { @@ -25,10 +25,10 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos GasPrototypes[i] = gasPrototype; if(string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayTexture)) - GasOverlays[i] = new SpriteSpecifier.Texture(new ResourcePath(gasPrototype.GasOverlayTexture)); + _gasOverlays[i] = new SpriteSpecifier.Texture(new ResourcePath(gasPrototype.GasOverlayTexture)); if(!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayState)) - GasOverlays[i] = new SpriteSpecifier.Rsi(new ResourcePath(gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState); + _gasOverlays[i] = new SpriteSpecifier.Rsi(new ResourcePath(gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState); } } @@ -38,6 +38,6 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos public IEnumerable Gases => GasPrototypes; - public SpriteSpecifier GetOverlay(int overlayId) => GasOverlays[overlayId]; + public SpriteSpecifier GetOverlay(int overlayId) => _gasOverlays[overlayId]; } } diff --git a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedGasTileOverlaySystem.cs b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedGasTileOverlaySystem.cs index 94b29f9c65..2e3af0e47b 100644 --- a/Content.Shared/GameObjects/EntitySystems/Atmos/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/Atmos/SharedGasTileOverlaySystem.cs @@ -20,16 +20,26 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos } [Serializable, NetSerializable] - public struct GasData + public readonly struct GasData : IEquatable { - public byte Index { get; set; } - public byte Opacity { get; set; } + public readonly byte Index; + public readonly byte Opacity; public GasData(byte gasId, byte opacity) { Index = gasId; Opacity = opacity; } + + public override int GetHashCode() + { + return HashCode.Combine(Index, Opacity); + } + + public bool Equals(GasData other) + { + return other.Index == Index && other.Opacity == Opacity; + } } [Serializable, NetSerializable] @@ -38,39 +48,48 @@ namespace Content.Shared.GameObjects.EntitySystems.Atmos public readonly byte FireState; public readonly float FireTemperature; public readonly GasData[] Gas; + public readonly int HashCode; public GasOverlayData(byte fireState, float fireTemperature, GasData[] gas) { FireState = fireState; FireTemperature = fireTemperature; - Gas = gas; + Gas = gas ?? Array.Empty(); + + Array.Sort(Gas, (a, b) => a.Index.CompareTo(b.Index)); + + var hash = new HashCode(); + hash.Add(FireState); + hash.Add(FireTemperature); + + foreach (var gasData in Gas) + { + hash.Add(gasData); + } + + HashCode = hash.ToHashCode(); + } + + public override int GetHashCode() + { + return HashCode; } public bool Equals(GasOverlayData other) { - // TODO: Moony had a suggestion on how to do this faster with the hash - // https://discordapp.com/channels/310555209753690112/310555209753690112/744080145219846204 - // Aside from that I can't really see any low-hanging fruit CPU perf wise. - if (Gas?.Length != other.Gas?.Length) return false; + + if (HashCode != other.HashCode) return false; + if (Gas.Length != other.Gas.Length) return false; if (FireState != other.FireState) return false; - if (FireTemperature != other.FireTemperature) return false; - - if (Gas == null) - { - return true; - } - - DebugTools.Assert(other.Gas != null); + if (MathHelper.CloseTo(FireTemperature, FireTemperature)) return false; + if (Gas.GetHashCode() != other.Gas.GetHashCode()) return false; for (var i = 0; i < Gas.Length; i++) { - var thisGas = Gas[i]; + var gas = Gas[i]; var otherGas = other.Gas[i]; - - if (!thisGas.Equals(otherGas)) - { + if (!gas.Equals(otherGas)) return false; - } } return true; diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 1cf66f57eb..79678476e7 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -25,6 +25,7 @@ namespace Content.Shared.Maps public float Friction { get; set; } public float ThermalConductivity { get; set; } public string ItemDropPrototypeName { get; private set; } + public bool IsSpace { get; private set; } public void AssignTileId(ushort id) { @@ -47,6 +48,11 @@ namespace Content.Shared.Maps else BaseTurfs = new List(); + if (mapping.TryGetNode("is_space", out node)) + { + IsSpace = node.AsBool(); + } + if (mapping.TryGetNode("can_crowbar", out node)) { CanCrowbar = node.AsBool(); diff --git a/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs index da3f322c57..329b18bd38 100644 --- a/Content.Shared/Maps/TurfHelpers.cs +++ b/Content.Shared/Maps/TurfHelpers.cs @@ -9,6 +9,7 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Physics; using Robust.Shared.IoC; +using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -16,15 +17,6 @@ namespace Content.Shared.Maps { public static class TurfHelpers { - /// - /// Returns the content tile definition for a tile. - /// - public static ContentTileDefinition GetContentTileDefinition(this Tile tile) - { - var tileDefinitionManager = IoCManager.Resolve(); - return (ContentTileDefinition)tileDefinitionManager[tile.TypeId]; - } - /// /// Attempts to get the turf at map indices with grid id or null if no such turf is found. /// @@ -72,6 +64,39 @@ namespace Content.Shared.Maps return (turf = coordinates.GetTileRef()) != null; } + /// + /// Returns the content tile definition for a tile. + /// + public static ContentTileDefinition GetContentTileDefinition(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null) + { + tileDefinitionManager ??= IoCManager.Resolve(); + return (ContentTileDefinition)tileDefinitionManager[tile.TypeId]; + } + + /// + /// Returns whether a tile is considered space. + /// + public static bool IsSpace(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null) + { + return tile.GetContentTileDefinition(tileDefinitionManager).IsSpace; + } + + /// + /// Returns the content tile definition for a tile ref. + /// + public static ContentTileDefinition GetContentTileDefinition(this TileRef tile, ITileDefinitionManager? tileDefinitionManager = null) + { + return tile.Tile.GetContentTileDefinition(tileDefinitionManager); + } + + /// + /// Returns whether a tile ref is considered space. + /// + public static bool IsSpace(this TileRef tile, ITileDefinitionManager? tileDefinitionManager = null) + { + return tile.Tile.IsSpace(tileDefinitionManager); + } + public static bool PryTile(this EntityCoordinates coordinates, IEntityManager? entityManager = null, IMapManager? mapManager = null) { diff --git a/Resources/Prototypes/Entities/Constructible/Power/computers.yml b/Resources/Prototypes/Entities/Constructible/Power/computers.yml index de05f9014d..6fca8c3106 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/computers.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/computers.yml @@ -52,15 +52,15 @@ - Impassable - MobImpassable - VaultImpassable - - type: Clickable - - type: InteractionOutline - - type: Anchorable - - type: Construction - graph: computer - node: monitorBroken - - type: Sprite - sprite: "Constructible/Power/computers.rsi" - state: "broken" + - type: Clickable + - type: InteractionOutline + - type: Anchorable + - type: Construction + graph: computer + node: monitorBroken + - type: Sprite + sprite: "Constructible/Power/computers.rsi" + state: "broken" - type: entity id: ComputerBase diff --git a/Resources/Prototypes/Tiles/plating.yml b/Resources/Prototypes/Tiles/plating.yml index 653b604980..cd9e3c5178 100644 --- a/Resources/Prototypes/Tiles/plating.yml +++ b/Resources/Prototypes/Tiles/plating.yml @@ -17,6 +17,7 @@ is_subfloor: true footstep_sounds: footstep_plating friction: 0.5 + is_space: true - type: tile name: underplating diff --git a/Resources/Prototypes/Tiles/space.yml b/Resources/Prototypes/Tiles/space.yml index 97a94447fe..6a0be3dc83 100644 --- a/Resources/Prototypes/Tiles/space.yml +++ b/Resources/Prototypes/Tiles/space.yml @@ -3,4 +3,5 @@ display_name: Space texture: "" friction: 0 - is_subfloor: true \ No newline at end of file + is_subfloor: true + is_space: true diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000000..dc1ae3b522 --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + +