Add a test that puts all components on an entity and checks for no exceptions (#1815)

* Add test that puts all components on an entity and checks for no exceptions

Also fix all the exceptions that happened because of this

* Add comments to the test

* Fix nullable errors

* Fix more nullable errors

* More nullable error fixes

* Unignore basic actor component

* Fix more nullable errors

* NULLABLE ERROR

* Add string interpolation

* Merge if checks

* Remove redundant pragma warning disable 649

* Address reviews

* Remove null wrappers around TryGetComponent

* Merge conflict fixes

* APC battery component error fix

* Fix power test

* Fix atmos mapgrid usages
This commit is contained in:
DrSmugleaf
2020-08-22 22:29:20 +02:00
committed by GitHub
parent c8178550b8
commit b9196d0a10
84 changed files with 1790 additions and 1123 deletions

View File

@@ -1,9 +1,10 @@
using System;
#nullable enable
using Content.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
@@ -13,7 +14,6 @@ namespace Content.Server.GameObjects.Components.Atmos
[RegisterComponent]
public class AirtightComponent : Component, IMapInit
{
private SnapGridComponent _snapGrid;
private (GridId, MapIndices) _lastPosition;
public override string Name => "Airtight";
@@ -28,7 +28,11 @@ namespace Content.Server.GameObjects.Components.Atmos
set
{
_airBlocked = value;
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?.Invalidate(_snapGrid.Position);
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
{
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?.Invalidate(snapGrid.Position);
}
}
}
@@ -48,19 +52,23 @@ namespace Content.Server.GameObjects.Components.Atmos
base.Initialize();
// Using the SnapGrid is critical for the performance of the room builder, and thus if
// it is absent the component will not be airtight. An exception is much easier to track
// down than the object magically not being airtight, so throw one if the SnapGrid component
// it is absent the component will not be airtight. A warning is much easier to track
// down than the object magically not being airtight, so log one if the SnapGrid component
// is missing.
if (!Owner.TryGetComponent(out _snapGrid))
throw new Exception("Airtight entities must have a SnapGrid component");
if (!Owner.EnsureComponent(out SnapGridComponent _))
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition.ToString()} doesn't have a {nameof(SnapGridComponent)}");
UpdatePosition();
}
public void MapInit()
{
_snapGrid.OnPositionChanged += OnTransformMove;
_lastPosition = (Owner.Transform.GridID, _snapGrid.Position);
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
{
snapGrid.OnPositionChanged += OnTransformMove;
_lastPosition = (Owner.Transform.GridID, snapGrid.Position);
}
UpdatePosition();
}
@@ -70,11 +78,15 @@ namespace Content.Server.GameObjects.Components.Atmos
_airBlocked = false;
_snapGrid.OnPositionChanged -= OnTransformMove;
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
{
snapGrid.OnPositionChanged -= OnTransformMove;
if (_fixVacuum)
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?
.FixVacuum(snapGrid.Position);
}
if(_fixVacuum)
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?
.FixVacuum(_snapGrid.Position);
UpdatePosition();
}
@@ -83,15 +95,24 @@ namespace Content.Server.GameObjects.Components.Atmos
{
UpdatePosition(_lastPosition.Item1, _lastPosition.Item2);
UpdatePosition();
_lastPosition = (Owner.Transform.GridID, _snapGrid.Position);
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
{
_lastPosition = (Owner.Transform.GridID, snapGrid.Position);
}
}
private void UpdatePosition() => UpdatePosition(Owner.Transform.GridID, _snapGrid.Position);
private void UpdatePosition()
{
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
{
UpdatePosition(Owner.Transform.GridID, snapGrid.Position);
}
}
private void UpdatePosition(GridId gridId, MapIndices pos)
{
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(gridId)?.Invalidate(pos);
}
}
}

View File

@@ -16,30 +16,37 @@ using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos
{
[RegisterComponent]
public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped, IUse
{
#pragma warning disable 649
[Dependency] private IServerNotifyManager _notifyManager = default!;
[Dependency] private IMapManager _mapManager = default!;
#pragma warning restore 649
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
private BoundUserInterface _userInterface = default!;
private GasAnalyzerDanger _pressureDanger;
private float _timeSinceSync;
private const float TimeBetweenSyncs = 2f;
private bool _checkPlayer = false; // Check at the player pos or at some other tile?
private GridCoordinates? _position; // The tile that we scanned
[ViewVariables]
private BoundUserInterface? UserInterface =>
Owner.TryGetComponent(out ServerUserInterfaceComponent? ui) &&
ui.TryGetBoundUserInterface(GasAnalyzerUiKey.Key, out var boundUi)
? boundUi
: null;
public override void Initialize()
{
base.Initialize();
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(GasAnalyzerUiKey.Key);
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
}
}
public override ComponentState GetComponentState()
@@ -56,7 +63,7 @@ namespace Content.Server.GameObjects.Components.Atmos
{
_checkPlayer = true;
_position = null;
_userInterface.Open(session);
UserInterface?.Open(session);
UpdateUserInterface();
Resync();
}
@@ -71,7 +78,7 @@ namespace Content.Server.GameObjects.Components.Atmos
{
_checkPlayer = false;
_position = pos;
_userInterface.Open(session);
UserInterface?.Open(session);
UpdateUserInterface();
Resync();
}
@@ -79,7 +86,7 @@ namespace Content.Server.GameObjects.Components.Atmos
public void CloseInterface(IPlayerSession session)
{
_position = null;
_userInterface.Close(session);
UserInterface?.Close(session);
Resync();
}
@@ -123,10 +130,15 @@ namespace Content.Server.GameObjects.Components.Atmos
private void UpdateUserInterface()
{
if (UserInterface == null)
{
return;
}
string? error = null;
// Check if the player is still holding the gas analyzer => if not, don't update
foreach (var session in _userInterface.SubscribedSessions)
foreach (var session in UserInterface.SubscribedSessions)
{
if (session.AttachedEntity == null)
return;
@@ -156,7 +168,7 @@ namespace Content.Server.GameObjects.Components.Atmos
if (tile == null)
{
error = "No Atmosphere!";
_userInterface.SetState(
UserInterface.SetState(
new GasAnalyzerBoundUserInterfaceState(
0,
0,
@@ -166,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Atmos
}
var gases = new List<GasEntry>();
for (int i = 0; i < Atmospherics.TotalNumberOfGases; i++)
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{
var gas = Atmospherics.GetGas(i);
@@ -175,7 +187,7 @@ namespace Content.Server.GameObjects.Components.Atmos
gases.Add(new GasEntry(gas.Name, tile.Gases[i], gas.Color));
}
_userInterface.SetState(
UserInterface.SetState(
new GasAnalyzerBoundUserInterfaceState(
tile.Pressure,
tile.Temperature,

View File

@@ -6,16 +6,21 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos
{
[RegisterComponent]
public class GasMixtureComponent : Component
public class GasMixtureHolderComponent : Component
{
public override string Name => "GasMixture";
public override string Name => "GasMixtureHolder";
[ViewVariables] public GasMixture GasMixture { get; set; } = new GasMixture();
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, x => GasMixture.Volume, "volume", 0f);
serializer.DataReadWriteFunction(
"volume",
0f,
vol => GasMixture.Volume = vol,
() => GasMixture.Volume);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -50,7 +51,6 @@ namespace Content.Server.GameObjects.Components.Atmos
private float _timer = 0f;
private Stopwatch _stopwatch = new Stopwatch();
public int UpdateCounter { get; private set; } = 0;
private IMapGrid _grid;
[ViewVariables]
private readonly HashSet<ExcitedGroup> _excitedGroups = new HashSet<ExcitedGroup>(1000);
@@ -89,42 +89,40 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
public void PryTile(MapIndices indices)
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGridComponent)) return;
if (IsSpace(indices) || IsAirBlocked(indices)) return;
var tile = _grid.GetTileRef(indices).Tile;
var mapGrid = mapGridComponent.Grid;
var tile = mapGrid.GetTileRef(indices).Tile;
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var tileDef = (ContentTileDefinition)tileDefinitionManager[tile.TypeId];
var underplating = tileDefinitionManager["underplating"];
_grid.SetTile(indices, new Tile(underplating.TileId));
mapGrid.SetTile(indices, new Tile(underplating.TileId));
//Actually spawn the relevant tile item at the right position and give it some offset to the corner.
var tileItem = IoCManager.Resolve<IServerEntityManager>().SpawnEntity(tileDef.ItemDropPrototypeName, new GridCoordinates(indices.X, indices.Y, _grid));
var tileItem = IoCManager.Resolve<IServerEntityManager>().SpawnEntity(tileDef.ItemDropPrototypeName, new GridCoordinates(indices.X, indices.Y, mapGrid));
tileItem.Transform.WorldPosition += (0.2f, 0.2f);
}
public override void Initialize()
{
base.Initialize();
_grid = Owner.GetComponent<IMapGridComponent>().Grid;
RepopulateTiles();
}
public override void OnAdd()
{
base.OnAdd();
_grid = Owner.GetComponent<IMapGridComponent>().Grid;
RepopulateTiles();
}
public void RepopulateTiles()
{
foreach (var tile in _grid.GetAllTiles())
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}));
@@ -145,6 +143,8 @@ namespace Content.Server.GameObjects.Components.Atmos
private void Revalidate()
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
foreach (var indices in _invalidatedCoords.ToArray())
{
var tile = GetTile(indices);
@@ -152,7 +152,7 @@ namespace Content.Server.GameObjects.Components.Atmos
if (tile == null)
{
tile = new TileAtmosphere(this, _grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
tile = new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
_tiles[indices] = tile;
}
@@ -199,8 +199,9 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
public void FixVacuum(MapIndices indices)
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
var tile = GetTile(indices);
if (tile?.GridIndex != _grid.Index) return;
if (tile?.GridIndex != mapGrid.Grid.Index) return;
var adjacent = GetAdjacentTiles(indices);
tile.Air = new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
_tiles[indices] = tile;
@@ -217,16 +218,17 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddActiveTile(TileAtmosphere tile)
public void AddActiveTile(TileAtmosphere? tile)
{
if (tile?.GridIndex != _grid.Index || tile?.Air == null) return;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
if (tile?.GridIndex != mapGrid.Grid.Index || tile?.Air == null) return;
tile.Excited = true;
_activeTiles.Add(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveActiveTile(TileAtmosphere tile)
public void RemoveActiveTile(TileAtmosphere? tile)
{
if (tile == null) return;
_activeTiles.Remove(tile);
@@ -236,27 +238,29 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHotspotTile(TileAtmosphere tile)
public void AddHotspotTile(TileAtmosphere? tile)
{
if (tile?.GridIndex != _grid.Index || tile?.Air == null) return;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
if (tile?.GridIndex != mapGrid.Grid.Index || tile?.Air == null) return;
_hotspotTiles.Add(tile);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveHotspotTile(TileAtmosphere tile)
public void RemoveHotspotTile(TileAtmosphere? tile)
{
if (tile == null) return;
_hotspotTiles.Remove(tile);
}
public void AddSuperconductivityTile(TileAtmosphere tile)
public void AddSuperconductivityTile(TileAtmosphere? tile)
{
if (tile?.GridIndex != _grid.Index) return;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
if (tile?.GridIndex != mapGrid.Grid.Index) return;
_superconductivityTiles.Add(tile);
}
public void RemoveSuperconductivityTile(TileAtmosphere tile)
public void RemoveSuperconductivityTile(TileAtmosphere? tile)
{
if (tile == null) return;
_superconductivityTiles.Remove(tile);
@@ -264,9 +268,10 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddHighPressureDelta(TileAtmosphere tile)
public void AddHighPressureDelta(TileAtmosphere? tile)
{
if (tile?.GridIndex != _grid.Index) return;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
if (tile?.GridIndex != mapGrid.Grid.Index) return;
_highPressureDelta.Add(tile);
}
@@ -292,20 +297,22 @@ namespace Content.Server.GameObjects.Components.Atmos
}
/// <inheritdoc />
public TileAtmosphere GetTile(GridCoordinates coordinates)
public TileAtmosphere? GetTile(GridCoordinates coordinates)
{
return GetTile(coordinates.ToMapIndices(_mapManager));
}
/// <inheritdoc />
public TileAtmosphere GetTile(MapIndices indices)
public TileAtmosphere? GetTile(MapIndices indices)
{
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return null;
if (_tiles.TryGetValue(indices, out var tile)) return tile;
// We don't have that tile!
if (IsSpace(indices))
{
var space = new TileAtmosphere(this, _grid.Index, indices, new GasMixture(int.MaxValue){Temperature = Atmospherics.TCMB});
var space = new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(int.MaxValue){Temperature = Atmospherics.TCMB});
space.Air.MarkImmutable();
return space;
}
@@ -324,7 +331,9 @@ namespace Content.Server.GameObjects.Components.Atmos
public bool IsSpace(MapIndices indices)
{
// TODO ATMOS use ContentTileDefinition to define in YAML whether or not a tile is considered space
return _grid.GetTileRef(indices).Tile.IsEmpty;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty;
}
public Dictionary<Direction, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false)
@@ -334,7 +343,7 @@ namespace Content.Server.GameObjects.Components.Atmos
{
var side = indices.Offset(dir);
var tile = GetTile(side);
if(tile?.Air != null || includeAirBlocked)
if (tile != null && (tile.Air != null || includeAirBlocked))
sides[dir] = tile;
}
@@ -349,7 +358,9 @@ namespace Content.Server.GameObjects.Components.Atmos
/// <inheritdoc />
public float GetVolumeForCells(int cellCount)
{
return _grid.TileSize * cellCount * Atmospherics.CellVolume;
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
return mapGrid.Grid.TileSize * cellCount * Atmospherics.CellVolume;
}
/// <inheritdoc />
@@ -509,9 +520,11 @@ namespace Content.Server.GameObjects.Components.Atmos
}
}
private AirtightComponent GetObstructingComponent(MapIndices indices)
private AirtightComponent? GetObstructingComponent(MapIndices indices)
{
foreach (var v in _grid.GetSnapGridCell(indices, SnapGridOffset.Center))
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
foreach (var v in mapGrid.Grid.GetSnapGridCell(indices, SnapGridOffset.Center))
{
if (v.Owner.TryGetComponent<AirtightComponent>(out var ac))
return ac;
@@ -534,22 +547,24 @@ namespace Content.Server.GameObjects.Components.Atmos
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
if (serializer.Reading)
if (serializer.Reading &&
Owner.TryGetComponent(out IMapGridComponent? mapGrid))
{
var gridId = Owner.GetComponent<IMapGridComponent>().Grid.Index;
var gridId = mapGrid.Grid.Index;
if (!serializer.TryReadDataField("uniqueMixes", out List<GasMixture> uniqueMixes) ||
!serializer.TryReadDataField("tiles", out Dictionary<MapIndices, int> tiles))
if (!serializer.TryReadDataField("uniqueMixes", out List<GasMixture>? uniqueMixes) ||
!serializer.TryReadDataField("tiles", out Dictionary<MapIndices, int>? tiles))
return;
_tiles.Clear();
foreach (var (indices, mix) in tiles)
foreach (var (indices, mix) in tiles!)
{
_tiles.Add(indices, new TileAtmosphere(this, gridId, indices, (GasMixture)uniqueMixes[mix].Clone()));
_tiles.Add(indices, new TileAtmosphere(this, gridId, indices, (GasMixture)uniqueMixes![mix].Clone()));
Invalidate(indices);
}
} else if (serializer.Writing)
}
else if (serializer.Writing)
{
var uniqueMixes = new List<GasMixture>();
var uniqueMixHash = new Dictionary<GasMixture, int>();