diff --git a/MSBuild/Robust.Engine.Version.props b/MSBuild/Robust.Engine.Version.props index 6fba408d005..61514327453 100644 --- a/MSBuild/Robust.Engine.Version.props +++ b/MSBuild/Robust.Engine.Version.props @@ -1,4 +1,4 @@ - 231.1.1 + 233.0.2 diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c341863d211..2444f05af62 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -40,10 +40,14 @@ END TEMPLATE--> ### New features * AddComponent now has an overload for ComponentRegistryEntry. +* `MarkupNode` is now `IEquatable`. It already supported equality checks, now it implements the interface. +* Added `Entity` overloads to the following `SharedMapSystem` methods: `GetTileRef`, `GetAnchoredEntities`, `TileIndicesFor`. +* Added `EntityUid`-only overloads to the following `SharedTransformSystem` methods: `AnchorEntity`, `Unanchor`. ### Bugfixes -*None yet* +* Fixed equality checks for `MarkupNode` not properly handling attributes. +* Fixed `MarkupNode` not having a `GetHashCode()` implementation. ### Other @@ -54,6 +58,64 @@ END TEMPLATE--> *None yet* +## 233.0.2 + +### Bugfixes + +* Fix exceptions in client game state handling for grids. Now they will rely upon the networked fixture data and not try to rebuild in the grid state handler. + + +## 233.0.1 + +### Bugfixes + +* Fix IsHardCollidable component to EntityUid references. + + +## 233.0.0 + +### Breaking changes + +* Made EntityRenamed a broadcast event & added additional args. +* Made test runs parallelizable. +* Added a debug assert that other threads aren't touching entities. + +### Bugfixes + +* Fix some entitylookup method transformations and add more tests. +* Fix mousehover not updating if new controls showed up under the mouse. + +### Internal + +* `ClientGameStateManager` now only initialises or starts entities after their parents have already been initialized. There are also some new debug asserts to try ensure that this rule isn't broken elsewhere. +* Engine version script now supports dashes. + + +## 232.0.0 + +### Breaking changes + +* Obsolete method `AppearanceComponent.TryGetData` is now access-restricted to `SharedAppearanceSystem`; use `SharedAppearanceSystem.TryGetData` instead. + +### New features + +* Added `SharedAppearanceSystem.AppendData`, which appends non-existing `AppearanceData` from one `AppearanceComponent` to another. +* Added `AppearanceComponent.AppearanceDataInit`, which can be used to set initial `AppearanceData` entries in .yaml. + +### Bugfixes + +* Fix BUI interfaces not deep-copying in state handling. +* Add Robust.Xaml.csproj to the solution to fix some XAML issues. + +### Other + +* Serialization will now add type tags (`!type:`) for necessary `NodeData` when writing (currently only for `object` nodes). + +### Internal + +* Added `ObjectSerializer`, which handles serialization of the generic `object` type. + + ## 231.1.1 ### Bugfixes diff --git a/Robust.Benchmarks/Physics/BoxStackBenchmark.cs b/Robust.Benchmarks/Physics/BoxStackBenchmark.cs new file mode 100644 index 00000000000..406264f54b3 --- /dev/null +++ b/Robust.Benchmarks/Physics/BoxStackBenchmark.cs @@ -0,0 +1,96 @@ +using System; +using System.Numerics; +using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.Configuration; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Systems; +using Robust.UnitTesting.Server; + +namespace Robust.Benchmarks.Physics; + +[Virtual] +[MediumRunJob] +public class PhysicsBoxStackBenchmark +{ + private ISimulation _sim = default!; + + [GlobalSetup] + public void Setup() + { + _sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + + var entManager = _sim.Resolve(); + entManager.System().CreateMap(out var mapId); + SetupTumbler(entManager, mapId); + + for (var i = 0; i < 30; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + [Benchmark] + public void BoxStack() + { + var entManager = _sim.Resolve(); + + for (var i = 0; i < 10000; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + private void SetupTumbler(IEntityManager entManager, MapId mapId) + { + var physics = entManager.System(); + var fixtures = entManager.System(); + + var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + var ground = entManager.AddComponent(groundUid); + + var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); + fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + + var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); + fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); + + var xs = new[] + { + 0.0f, -10.0f, -5.0f, 5.0f, 10.0f + }; + + var columnCount = 1; + var rowCount = 15; + PolygonShape shape; + + for (var j = 0; j < columnCount; j++) + { + for (var i = 0; i < rowCount; i++) + { + var x = 0.0f; + + var boxUid = entManager.SpawnEntity(null, + new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 1.1f * i), mapId)); + var box = entManager.AddComponent(boxUid); + + physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); + + shape = new PolygonShape(); + shape.SetAsBox(0.5f, 0.5f); + physics.SetFixedRotation(boxUid, false, body: box); + fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box); + + physics.WakeBody(boxUid, body: box); + physics.SetSleepingAllowed(boxUid, box, false); + } + } + + physics.WakeBody(groundUid, body: ground); + } +} diff --git a/Robust.Benchmarks/Physics/CircleStackBenchmark.cs b/Robust.Benchmarks/Physics/CircleStackBenchmark.cs new file mode 100644 index 00000000000..eb79a6b9a53 --- /dev/null +++ b/Robust.Benchmarks/Physics/CircleStackBenchmark.cs @@ -0,0 +1,92 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Systems; +using Robust.UnitTesting.Server; + +namespace Robust.Benchmarks.Physics; + +[Virtual] +public class PhysicsCircleStackBenchmark +{ + private ISimulation _sim = default!; + + [GlobalSetup] + public void Setup() + { + _sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + + var entManager = _sim.Resolve(); + entManager.System().CreateMap(out var mapId); + SetupTumbler(entManager, mapId); + + for (var i = 0; i < 30; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + [Benchmark] + public void CircleStack() + { + var entManager = _sim.Resolve(); + + for (var i = 0; i < 10000; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + private void SetupTumbler(IEntityManager entManager, MapId mapId) + { + var physics = entManager.System(); + var fixtures = entManager.System(); + + var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + var ground = entManager.AddComponent(groundUid); + + var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); + fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + + var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20)); + fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); + + var xs = new[] + { + 0.0f, -10.0f, -5.0f, 5.0f, 10.0f + }; + + var columnCount = 1; + var rowCount = 15; + PhysShapeCircle shape; + + for (var j = 0; j < columnCount; j++) + { + for (var i = 0; i < rowCount; i++) + { + var x = 0.0f; + + var boxUid = entManager.SpawnEntity(null, + new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)); + var box = entManager.AddComponent(boxUid); + + physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); + shape = new PhysShapeCircle(0.5f); + physics.SetFixedRotation(boxUid, false, body: box); + // TODO: Need to detect shape and work out if we need to use fixedrotation + + fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f)); + physics.WakeBody(boxUid, body: box); + physics.SetSleepingAllowed(boxUid, box, false); + } + } + + physics.WakeBody(groundUid, body: ground); + } +} diff --git a/Robust.Benchmarks/Physics/PyramidBenchmark.cs b/Robust.Benchmarks/Physics/PyramidBenchmark.cs new file mode 100644 index 00000000000..505176dfa65 --- /dev/null +++ b/Robust.Benchmarks/Physics/PyramidBenchmark.cs @@ -0,0 +1,91 @@ +using System; +using System.Numerics; +using BenchmarkDotNet.Attributes; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Systems; +using Robust.UnitTesting.Server; + +namespace Robust.Benchmarks.Physics; + +[Virtual] +public class PhysicsPyramidBenchmark +{ + private ISimulation _sim = default!; + + [GlobalSetup] + public void Setup() + { + _sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + + var entManager = _sim.Resolve(); + entManager.System().CreateMap(out var mapId); + SetupTumbler(entManager, mapId); + + for (var i = 0; i < 300; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + [Benchmark] + public void Pyramid() + { + var entManager = _sim.Resolve(); + + for (var i = 0; i < 5000; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + private void SetupTumbler(IEntityManager entManager, MapId mapId) + { + const byte count = 20; + + // Setup ground + var physics = entManager.System(); + var fixtures = entManager.System(); + var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); + var ground = entManager.AddComponent(groundUid); + + var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0)); + fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); + physics.WakeBody(groundUid, body: ground); + + // Setup boxes + float a = 0.5f; + PolygonShape shape = new(); + shape.SetAsBox(a, a); + + var x = new Vector2(-7.0f, 0.75f); + Vector2 y; + Vector2 deltaX = new Vector2(0.5625f, 1.25f); + Vector2 deltaY = new Vector2(1.125f, 0.0f); + + for (var i = 0; i < count; ++i) + { + y = x; + + for (var j = i; j < count; ++j) + { + var boxUid = entManager.SpawnEntity(null, new MapCoordinates(y, mapId)); + var box = entManager.AddComponent(boxUid); + physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); + + fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box); + y += deltaY; + + physics.WakeBody(boxUid, body: box); + physics.SetSleepingAllowed(boxUid, box, false); + } + + x += deltaX; + } + } +} diff --git a/Robust.Benchmarks/Physics/TumblerBenchmark.cs b/Robust.Benchmarks/Physics/TumblerBenchmark.cs new file mode 100644 index 00000000000..f2e44185be6 --- /dev/null +++ b/Robust.Benchmarks/Physics/TumblerBenchmark.cs @@ -0,0 +1,105 @@ +using System; +using System.Numerics; +using BenchmarkDotNet.Attributes; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Systems; +using Robust.UnitTesting.Server; + +namespace Robust.Benchmarks.Physics; + +[Virtual] +public class PhysicsTumblerBenchmark +{ + private ISimulation _sim = default!; + + [GlobalSetup] + public void Setup() + { + _sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + + var entManager = _sim.Resolve(); + var physics = entManager.System(); + var fixtures = entManager.System(); + entManager.System().CreateMap(out var mapId); + SetupTumbler(entManager, mapId); + + for (var i = 0; i < 800; i++) + { + entManager.TickUpdate(0.016f, false); + var boxUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId)); + var box = entManager.AddComponent(boxUid); + physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); + physics.SetFixedRotation(boxUid, false, body: box); + var shape = new PolygonShape(); + shape.SetAsBox(0.125f, 0.125f); + fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box); + physics.WakeBody(boxUid, body: box); + physics.SetSleepingAllowed(boxUid, box, false); + } + } + + [Benchmark] + public void Tumbler() + { + var entManager = _sim.Resolve(); + + for (var i = 0; i < 5000; i++) + { + entManager.TickUpdate(0.016f, false); + } + } + + private void SetupTumbler(IEntityManager entManager, MapId mapId) + { + var physics = entManager.System(); + var fixtures = entManager.System(); + var joints = entManager.System(); + + var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId)); + var ground = entManager.AddComponent(groundUid); + // Due to lookup changes fixtureless bodies are invalid, so + var cShape = new PhysShapeCircle(1f); + fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false)); + + var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId)); + var body = entManager.AddComponent(bodyUid); + + physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body); + physics.SetSleepingAllowed(bodyUid, body, false); + physics.SetFixedRotation(bodyUid, false, body: body); + + + // TODO: Box2D just deref, bleh shape structs someday + var shape1 = new PolygonShape(); + shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f); + fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f)); + + var shape2 = new PolygonShape(); + shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f); + fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f)); + + var shape3 = new PolygonShape(); + shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f); + fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f)); + + var shape4 = new PolygonShape(); + shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f); + fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f)); + + physics.WakeBody(groundUid, body: ground); + physics.WakeBody(bodyUid, body: body); + var revolute = joints.CreateRevoluteJoint(groundUid, bodyUid); + revolute.LocalAnchorA = new Vector2(0f, 10f); + revolute.LocalAnchorB = new Vector2(0f, 0f); + revolute.ReferenceAngle = 0f; + revolute.MotorSpeed = 0.05f * MathF.PI; + revolute.MaxMotorTorque = 100000000f; + revolute.EnableMotor = true; + } +} diff --git a/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs b/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs index 46ec41efa53..d646d34541a 100644 --- a/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs @@ -17,7 +17,6 @@ namespace Robust.Client.GameObjects { public sealed class ContainerSystem : SharedContainerSystem { - [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly IRobustSerializer _serializer = default!; [Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!; [Dependency] private readonly PointLightSystem _lightSys = default!; diff --git a/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs b/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs index 0472ec6f283..15d469ef76f 100644 --- a/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/GridChunkBoundsDebugSystem.cs @@ -82,6 +82,7 @@ protected internal override void Draw(in OverlayDrawArgs args) var viewport = args.WorldBounds; var worldHandle = args.WorldHandle; + var fixturesQuery = _entityManager.GetEntityQuery(); _grids.Clear(); _mapManager.FindGridsIntersecting(currentMap, viewport, ref _grids); foreach (var grid in _grids) @@ -89,13 +90,15 @@ protected internal override void Draw(in OverlayDrawArgs args) var worldMatrix = _transformSystem.GetWorldMatrix(grid); worldHandle.SetTransform(worldMatrix); var transform = new Transform(Vector2.Zero, Angle.Zero); + var fixtures = fixturesQuery.Comp(grid.Owner); var chunkEnumerator = _mapSystem.GetMapChunks(grid.Owner, grid.Comp, viewport); while (chunkEnumerator.MoveNext(out var chunk)) { - foreach (var fixture in chunk.Fixtures.Values) + foreach (var id in chunk.Fixtures) { + var fixture = fixtures.Fixtures[id]; var poly = (PolygonShape) fixture.Shape; var verts = new Vector2[poly.VertexCount]; diff --git a/Robust.Client/GameObjects/EntitySystems/InputSystem.cs b/Robust.Client/GameObjects/EntitySystems/InputSystem.cs index 8bc5cca0d3e..2ec62ceda42 100644 --- a/Robust.Client/GameObjects/EntitySystems/InputSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/InputSystem.cs @@ -20,7 +20,6 @@ namespace Robust.Client.GameObjects /// public sealed class InputSystem : SharedInputSystem, IPostInjectInit { - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IClientGameStateManager _stateManager = default!; diff --git a/Robust.Client/GameStates/ClientGameStateManager.cs b/Robust.Client/GameStates/ClientGameStateManager.cs index f1d01566878..30240d7abb5 100644 --- a/Robust.Client/GameStates/ClientGameStateManager.cs +++ b/Robust.Client/GameStates/ClientGameStateManager.cs @@ -960,7 +960,7 @@ public IEnumerable ApplyGameState(GameState curState, GameState? next // Initialize and start the newly created entities. if (_toCreate.Count > 0) - InitializeAndStart(_toCreate); + InitializeAndStart(_toCreate, metas, xforms); _prof.WriteValue("State Size", ProfData.Int32(curSpan.Length)); _prof.WriteValue("Entered PVS", ProfData.Int32(enteringPvs)); @@ -1188,7 +1188,10 @@ private void Detach(GameTick maxTick, } } - private void InitializeAndStart(Dictionary toCreate) + private void InitializeAndStart( + Dictionary toCreate, + EntityQuery metas, + EntityQuery xforms) { _toStart.Clear(); @@ -1197,22 +1200,8 @@ private void InitializeAndStart(Dictionary toCreate) EntityUid entity = default; foreach (var netEntity in toCreate.Keys) { - try - { - (entity, var meta) = _entityManager.GetEntityData(netEntity); - _entities.InitializeEntity(entity, meta); - _toStart.Add((entity, netEntity)); - } - catch (Exception e) - { - _sawmill.Error($"Server entity threw in Init: nent={netEntity}, ent={_entities.ToPrettyString(entity)}"); - _runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}"); - _toCreate.Remove(netEntity); - _brokenEnts.Add(entity); -#if !EXCEPTION_TOLERANCE - throw; -#endif - } + (entity, var meta) = _entityManager.GetEntityData(netEntity); + InitializeRecursive(entity, meta, metas, xforms); } } @@ -1244,6 +1233,44 @@ private void InitializeAndStart(Dictionary toCreate) _brokenEnts.Clear(); } + private void InitializeRecursive( + EntityUid entity, + MetaDataComponent meta, + EntityQuery metas, + EntityQuery xforms) + { + var xform = xforms.GetComponent(entity); + if (xform.ParentUid is {Valid: true} parent) + { + var parentMeta = metas.GetComponent(parent); + if (parentMeta.EntityLifeStage < EntityLifeStage.Initialized) + InitializeRecursive(parent, parentMeta, metas, xforms); + } + + if (meta.EntityLifeStage >= EntityLifeStage.Initialized) + { + // Was probably already initialized because one of its children appeared earlier in the list. + DebugTools.AssertEqual(_toStart.Count(x => x.Item1 == entity), 1); + return; + } + + try + { + _entities.InitializeEntity(entity, meta); + _toStart.Add((entity, meta.NetEntity)); + } + catch (Exception e) + { + _sawmill.Error($"Server entity threw in Init: nent={meta.NetEntity}, ent={_entities.ToPrettyString(entity)}"); + _runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}"); + _toCreate.Remove(meta.NetEntity); + _brokenEnts.Add(entity); +#if !EXCEPTION_TOLERANCE + throw; +#endif + } + } + private void HandleEntityState(EntityUid uid, NetEntity netEntity, MetaDataComponent meta, IEventBus bus, EntityState? curState, EntityState? nextState, GameTick lastApplied, GameTick toTick, bool enteringPvs) { diff --git a/Robust.Client/GameStates/NetInterpOverlay.cs b/Robust.Client/GameStates/NetInterpOverlay.cs index 1dd3d7cef38..b1cbc02b8ca 100644 --- a/Robust.Client/GameStates/NetInterpOverlay.cs +++ b/Robust.Client/GameStates/NetInterpOverlay.cs @@ -15,7 +15,6 @@ internal sealed class NetInterpOverlay : Overlay { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly EntityLookupSystem _lookup; diff --git a/Robust.Client/Graphics/Clyde/Clyde.cs b/Robust.Client/Graphics/Clyde/Clyde.cs index a1e2484c441..736116e5abc 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.cs @@ -32,7 +32,6 @@ namespace Robust.Client.Graphics.Clyde internal sealed partial class Clyde : IClydeInternal, IPostInjectInit, IEntityEventSubscriber { [Dependency] private readonly IClydeTileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly ILightManager _lightManager = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; diff --git a/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs b/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs index ec3b9f8c02e..656f5b80a25 100644 --- a/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs +++ b/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs @@ -23,7 +23,7 @@ public sealed partial class ReplayLoadManager // Scratch data used by UpdateEntityStates. // Avoids copying changes for every change to an entity between checkpoints, instead copies once per checkpoint on // first change. We can also use this to avoid building a dictionary of ComponentChange inside the inner loop. - private class UpdateScratchData + private sealed class UpdateScratchData { public Dictionary Changes; public EntityState lastChange; diff --git a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs index 66b11f6ffa1..70eab2f376e 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs @@ -21,7 +21,6 @@ public sealed class EntitySpawningUIController : UIController { [Dependency] private readonly IPlacementManager _placement = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; - [Dependency] private readonly IResourceCache _resources = default!; private EntitySpawnWindow? _window; private readonly List _shownEntities = new(); diff --git a/Robust.Client/UserInterface/UserInterfaceManager.Input.cs b/Robust.Client/UserInterface/UserInterfaceManager.Input.cs index a82b4c2a133..2b647131fc0 100644 --- a/Robust.Client/UserInterface/UserInterfaceManager.Input.cs +++ b/Robust.Client/UserInterface/UserInterfaceManager.Input.cs @@ -148,7 +148,7 @@ public void MouseMove(MouseMoveEventArgs mouseMoveEventArgs) var newHovered = MouseGetControl(mouseMoveEventArgs.Position); SetHovered(newHovered); - var target = ControlFocused ?? newHovered; + var target = ControlFocused ?? CurrentlyHovered; if (target != null) { var pos = mouseMoveEventArgs.Position.Position; @@ -164,7 +164,7 @@ public void MouseMove(MouseMoveEventArgs mouseMoveEventArgs) public void UpdateHovered() { - var ctrl = MouseGetControl(_inputManager.MouseScreenPosition); + var ctrl = MouseGetControl(_inputManager.MouseScreenPosition); SetHovered(ctrl); } diff --git a/Robust.Client/UserInterface/UserInterfaceManager.cs b/Robust.Client/UserInterface/UserInterfaceManager.cs index fd4bc38e640..af7c1eb6afe 100644 --- a/Robust.Client/UserInterface/UserInterfaceManager.cs +++ b/Robust.Client/UserInterface/UserInterfaceManager.cs @@ -215,6 +215,9 @@ public void FrameUpdate(FrameEventArgs args) { using (_prof.Group("Update")) { + // Update hovered. Can't rely upon mouse movement due to New controls potentially coming up. + UpdateHovered(); + foreach (var root in _roots) { CheckRootUIScaleUpdate(root); diff --git a/Robust.Server/Console/Commands/TestbedCommand.cs b/Robust.Server/Console/Commands/TestbedCommand.cs deleted file mode 100644 index 37a3f8c44c3..00000000000 --- a/Robust.Server/Console/Commands/TestbedCommand.cs +++ /dev/null @@ -1,335 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -using System; -using System.Numerics; -using Robust.Server.Player; -using Robust.Shared.Console; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Maths; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Controllers; -using Robust.Shared.Physics.Dynamics; -using Robust.Shared.Physics.Dynamics.Joints; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; - -namespace Robust.Server.Console.Commands -{ - /* - * I didn't use blueprints because this is way easier to iterate upon as I can shit out testbed upon testbed on new maps - * and never have to leave my debugger. - */ - - /// - /// Copies of Box2D's physics testbed for debugging. - /// - public sealed class TestbedCommand : LocalizedCommands - { - [Dependency] private readonly IEntityManager _ent = default!; - [Dependency] private readonly IMapManager _map = default!; - - public override string Command => "testbed"; - - public override void Execute(IConsoleShell shell, string argStr, string[] args) - { - if (args.Length != 2) - { - shell.WriteError("Require 2 args for testbed!"); - return; - } - - if (!int.TryParse(args[0], out var mapInt)) - { - shell.WriteError($"Unable to parse map {args[0]}"); - return; - } - - var mapId = new MapId(mapInt); - if (!_map.MapExists(mapId)) - { - shell.WriteError($"map {args[0]} does not exist"); - return; - } - - if (shell.Player == null) - { - shell.WriteError("No player found"); - return; - } - - Action testbed; - SetupPlayer(mapId, shell); - - switch (args[1]) - { - case "boxstack": - testbed = () => CreateBoxStack(mapId); - break; - case "circlestack": - testbed = () => CreateCircleStack(mapId); - break; - case "pyramid": - testbed = () => CreatePyramid(mapId); - break; - case "tumbler": - testbed = () => CreateTumbler(mapId); - break; - default: - shell.WriteError($"testbed {args[0]} not found!"); - return; - } - - Timer.Spawn(1000, () => - { - if (!_map.MapExists(mapId)) return; - testbed(); - }); - - shell.WriteLine($"Testbed on map {mapId}"); - } - - private void SetupPlayer(MapId mapId, IConsoleShell shell) - { - _map.SetMapPaused(mapId, false); - var mapUid = _map.GetMapEntityIdOrThrow(mapId); - _ent.System().SetGravity(mapUid, new Vector2(0, -9.8f)); - - shell.ExecuteCommand("aghost"); - shell.ExecuteCommand($"tp 0 0 {mapId}"); - shell.RemoteExecuteCommand($"physics shapes"); - - return; - } - - private void CreateBoxStack(MapId mapId) - { - var physics = _ent.System(); - var fixtures = _ent.System(); - - var groundUid = _ent.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); - var ground = _ent.AddComponent(groundUid); - - var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); - - var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); - fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); - - var xs = new[] - { - 0.0f, -10.0f, -5.0f, 5.0f, 10.0f - }; - - var columnCount = 1; - var rowCount = 15; - PolygonShape shape; - - for (var j = 0; j < columnCount; j++) - { - for (var i = 0; i < rowCount; i++) - { - var x = 0.0f; - - var boxUid = _ent.SpawnEntity(null, - new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 1.1f * i), mapId)); - var box = _ent.AddComponent(boxUid); - - physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); - - shape = new PolygonShape(); - shape.SetAsBox(0.5f, 0.5f); - physics.SetFixedRotation(boxUid, false, body: box); - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box); - - physics.WakeBody(boxUid, body: box); - } - } - - physics.WakeBody(groundUid, body: ground); - } - - private void CreateCircleStack(MapId mapId) - { - var physics = _ent.System(); - var fixtures = _ent.System(); - - var groundUid = _ent.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); - var ground = _ent.AddComponent(groundUid); - - var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); - - var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20)); - fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground); - - var xs = new[] - { - 0.0f, -10.0f, -5.0f, 5.0f, 10.0f - }; - - var columnCount = 1; - var rowCount = 15; - PhysShapeCircle shape; - - for (var j = 0; j < columnCount; j++) - { - for (var i = 0; i < rowCount; i++) - { - var x = 0.0f; - - var boxUid = _ent.SpawnEntity(null, - new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)); - var box = _ent.AddComponent(boxUid); - - physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); - shape = new PhysShapeCircle(0.5f); - physics.SetFixedRotation(boxUid, false, body: box); - // TODO: Need to detect shape and work out if we need to use fixedrotation - - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f)); - physics.WakeBody(boxUid, body: box); - } - } - - physics.WakeBody(groundUid, body: ground); - } - - private void CreatePyramid(MapId mapId) - { - const byte count = 20; - - // Setup ground - var physics = _ent.System(); - var fixtures = _ent.System(); - var groundUid = _ent.SpawnEntity(null, new MapCoordinates(0, 0, mapId)); - var ground = _ent.AddComponent(groundUid); - - var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0)); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground); - physics.WakeBody(groundUid, body: ground); - - // Setup boxes - float a = 0.5f; - PolygonShape shape = new(); - shape.SetAsBox(a, a); - - var x = new Vector2(-7.0f, 0.75f); - Vector2 y; - Vector2 deltaX = new Vector2(0.5625f, 1.25f); - Vector2 deltaY = new Vector2(1.125f, 0.0f); - - for (var i = 0; i < count; ++i) - { - y = x; - - for (var j = i; j < count; ++j) - { - var boxUid = _ent.SpawnEntity(null, new MapCoordinates(y, mapId)); - var box = _ent.AddComponent(boxUid); - physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); - - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box); - y += deltaY; - - physics.WakeBody(boxUid, body: box); - } - - x += deltaX; - } - } - - private void CreateTumbler(MapId mapId) - { - var physics = _ent.System(); - var fixtures = _ent.System(); - var joints = _ent.System(); - - var groundUid = _ent.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId)); - var ground = _ent.AddComponent(groundUid); - // Due to lookup changes fixtureless bodies are invalid, so - var cShape = new PhysShapeCircle(1f); - fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false)); - - var bodyUid = _ent.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId)); - var body = _ent.AddComponent(bodyUid); - - physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body); - physics.SetSleepingAllowed(bodyUid, body, false); - physics.SetFixedRotation(bodyUid, false, body: body); - - - // TODO: Box2D just deref, bleh shape structs someday - var shape1 = new PolygonShape(); - shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f); - fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f)); - - var shape2 = new PolygonShape(); - shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f)); - - var shape3 = new PolygonShape(); - shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f)); - - var shape4 = new PolygonShape(); - shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f); - fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f)); - - physics.WakeBody(groundUid, body: ground); - physics.WakeBody(bodyUid, body: body); - var revolute = joints.CreateRevoluteJoint(groundUid, bodyUid); - revolute.LocalAnchorA = new Vector2(0f, 10f); - revolute.LocalAnchorB = new Vector2(0f, 0f); - revolute.ReferenceAngle = 0f; - revolute.MotorSpeed = 0.05f * MathF.PI; - revolute.MaxMotorTorque = 100000000f; - revolute.EnableMotor = true; - - // Box2D has this as 800 which is jesus christo. - // Wouldn't recommend higher than 100 in debug and higher than 300 on release unless - // you really want a profile. - var count = 300; - - for (var i = 0; i < count; i++) - { - Timer.Spawn(i * 20, () => - { - if (!_map.MapExists(mapId)) return; - var boxUid = _ent.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId)); - var box = _ent.AddComponent(boxUid); - physics.SetBodyType(boxUid, BodyType.Dynamic, body: box); - physics.SetFixedRotation(boxUid, false, body: box); - var shape = new PolygonShape(); - shape.SetAsBox(0.125f, 0.125f); - fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box); - physics.WakeBody(boxUid, body: box); - }); - } - } - } -} diff --git a/Robust.Server/GameObjects/ServerEntityManager.cs b/Robust.Server/GameObjects/ServerEntityManager.cs index 000cbc6a2e9..ead2ee7e148 100644 --- a/Robust.Server/GameObjects/ServerEntityManager.cs +++ b/Robust.Server/GameObjects/ServerEntityManager.cs @@ -81,6 +81,7 @@ void IServerEntityManagerInternal.FinishEntityInitialization(EntityUid entity, M InitializeEntity(entity, meta); } + [Obsolete("Use StartEntity")] void IServerEntityManagerInternal.FinishEntityStartup(EntityUid entity) { StartEntity(entity); diff --git a/Robust.Server/Toolshed/Commands/Players/PlayersCommand.cs b/Robust.Server/Toolshed/Commands/Players/PlayersCommand.cs index 6ff6c1040a4..6b82b005644 100644 --- a/Robust.Server/Toolshed/Commands/Players/PlayersCommand.cs +++ b/Robust.Server/Toolshed/Commands/Players/PlayersCommand.cs @@ -81,7 +81,7 @@ public record struct NoSuchPlayerError(string Username) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup($"No player with the username/GUID {Username} could be found."); + return FormattedMessage.FromUnformatted($"No player with the username/GUID {Username} could be found."); } public string? Expression { get; set; } diff --git a/Robust.Shared/Containers/ContainerManagerComponent.cs b/Robust.Shared/Containers/ContainerManagerComponent.cs index b96ac472b3d..e86e266a42b 100644 --- a/Robust.Shared/Containers/ContainerManagerComponent.cs +++ b/Robust.Shared/Containers/ContainerManagerComponent.cs @@ -19,7 +19,6 @@ namespace Robust.Shared.Containers [RegisterComponent, ComponentProtoName("ContainerContainer")] public sealed partial class ContainerManagerComponent : Component, ISerializationHooks { - [Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!; [Dependency] private readonly IEntityManager _entMan = default!; [DataField("containers")] diff --git a/Robust.Shared/GameObjects/Components/Appearance/AppearanceComponent.cs b/Robust.Shared/GameObjects/Components/Appearance/AppearanceComponent.cs index 8816d055227..daef024d494 100644 --- a/Robust.Shared/GameObjects/Components/Appearance/AppearanceComponent.cs +++ b/Robust.Shared/GameObjects/Components/Appearance/AppearanceComponent.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameStates; +using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; namespace Robust.Shared.GameObjects; @@ -15,6 +16,7 @@ namespace Robust.Shared.GameObjects; /// Visualization works client side with derivatives of the VisualizerSystem class and corresponding components. /// [RegisterComponent, NetworkedComponent] +[Access(typeof(SharedAppearanceSystem))] public sealed partial class AppearanceComponent : Component { /// @@ -32,6 +34,19 @@ public sealed partial class AppearanceComponent : Component [ViewVariables] internal Dictionary AppearanceData = new(); + private Dictionary? _appearanceDataInit; + + /// + /// Sets starting values for AppearanceData. + /// + /// + /// Should only be filled in via prototype .yaml; subsequent data must be set via SharedAppearanceSystem.SetData(). + /// + [DataField(readOnly: true)] public Dictionary? AppearanceDataInit { + get { return _appearanceDataInit; } + set { AppearanceData = value ?? AppearanceData; _appearanceDataInit = value; } + } + [Obsolete("Use SharedAppearanceSystem instead")] public bool TryGetData(Enum key, [NotNullWhen(true)] out T data) { diff --git a/Robust.Shared/GameObjects/Components/UserInterface/UserInterfaceComponent.cs b/Robust.Shared/GameObjects/Components/UserInterface/UserInterfaceComponent.cs index de5a3f3bb1a..5e5efed8f76 100644 --- a/Robust.Shared/GameObjects/Components/UserInterface/UserInterfaceComponent.cs +++ b/Robust.Shared/GameObjects/Components/UserInterface/UserInterfaceComponent.cs @@ -69,9 +69,11 @@ public sealed partial class InterfaceData [DataField] public bool RequireInputValidation = true; - public InterfaceData(string type) + public InterfaceData(InterfaceData data) { - ClientType = type; + ClientType = data.ClientType; + InteractionRange = data.InteractionRange; + RequireInputValidation = data.RequireInputValidation; } } diff --git a/Robust.Shared/GameObjects/EntityManager.Components.cs b/Robust.Shared/GameObjects/EntityManager.Components.cs index 29e370523f0..2ab8298cd4e 100644 --- a/Robust.Shared/GameObjects/EntityManager.Components.cs +++ b/Robust.Shared/GameObjects/EntityManager.Components.cs @@ -110,6 +110,7 @@ public int Count(Type component) return dict.Count; } + [Obsolete("Use InitializeEntity")] public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = null) { DebugTools.AssertOwner(uid, metadata); @@ -145,6 +146,7 @@ public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = nu SetLifeStage(metadata, EntityLifeStage.Initialized); } + [Obsolete("Use StartEntity")] public void StartComponents(EntityUid uid) { // Startup() can modify _components @@ -342,6 +344,8 @@ private void AddComponentInternal(EntityUid uid, T component, bool overwrite, private void AddComponentInternal(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent metadata) where T : IComponent { + ThreadCheck(); + // We can't use typeof(T) here in case T is just Component DebugTools.Assert(component is MetaDataComponent || (metadata ?? MetaQuery.GetComponent(uid)).EntityLifeStage < EntityLifeStage.Terminating, @@ -602,6 +606,8 @@ private void RemoveComponentImmediate( bool terminating, MetaDataComponent? meta) { + ThreadCheck(); + if (component.Deleted) { _sawmill.Warning($"Deleting an already deleted component. Entity: {ToPrettyString(uid)}, Component: {_componentFactory.GetComponentName(component.GetType())}."); diff --git a/Robust.Shared/GameObjects/EntityManager.cs b/Robust.Shared/GameObjects/EntityManager.cs index c854bf9e869..7ba519b7eae 100644 --- a/Robust.Shared/GameObjects/EntityManager.cs +++ b/Robust.Shared/GameObjects/EntityManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; @@ -117,6 +118,10 @@ public abstract partial class EntityManager : IEntityManager public bool Initialized { get; protected set; } +#if DEBUG + private int _mainThreadId; +#endif + /// /// Constructs a new instance of . /// @@ -138,6 +143,10 @@ public virtual void Initialize() _sawmill = LogManager.GetSawmill("entity"); _resolveSawmill = LogManager.GetSawmill("resolve"); +#if DEBUG + _mainThreadId = Environment.CurrentManagedThreadId; +#endif + Initialized = true; } @@ -511,6 +520,8 @@ public void DeleteEntity(EntityUid e, MetaDataComponent meta, TransformComponent if (!Started) return; + ThreadCheck(); + if (meta.EntityLifeStage >= EntityLifeStage.Deleted) return; @@ -752,6 +763,8 @@ private protected EntityUid AllocEntity( /// private EntityUid AllocEntity(out MetaDataComponent metadata) { + ThreadCheck(); + var uid = GenerateEntityUid(); #if DEBUG @@ -864,15 +877,35 @@ public void InitializeAndStartEntity(Entity entity, bool doM public void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null) { + // Ideally, entities only ever get initialized once their parent has already been initialized. + // Note that this doesn't guarantee that an uninitialized entity will never have initialized children. + // In particular, for the client this might happen when applying a new game state that re-parents an + // existing entity to a newly created entity. The new entity only gets initialiuzed & started at the end, + // after the old/existing entity was already moved to the new parent. + DebugTools.Assert(TransformQuery.GetComponent(entity).ParentUid is not { Valid: true } parent + || MetaQuery.GetComponent(parent).EntityLifeStage >= EntityLifeStage.Initialized); + DebugTools.AssertOwner(entity, meta); meta ??= GetComponent(entity); +#pragma warning disable CS0618 // Type or member is obsolete InitializeComponents(entity, meta); +#pragma warning restore CS0618 // Type or member is obsolete EntityInitialized?.Invoke((entity, meta)); } public void StartEntity(EntityUid entity) { + // Ideally, entities only ever get initialized once their parent has already been initialized. + // Note that this doesn't guarantee that an uninitialized entity will never have initialized children. + // In particular, for the client this might happen when applying a new game state that re-parents an + // existing entity to a newly created entity. The new entity only gets initialiuzed & started at the end, + // after the old/existing entity was already moved to the new parent. + DebugTools.Assert(TransformQuery.GetComponent(entity).ParentUid is not { Valid: true } parent + || MetaQuery.GetComponent(parent).EntityLifeStage >= EntityLifeStage.Initialized); + +#pragma warning disable CS0618 // Type or member is obsolete StartComponents(entity); +#pragma warning restore CS0618 // Type or member is obsolete } public void RunMapInit(EntityUid entity, MetaDataComponent meta) @@ -953,6 +986,16 @@ internal EntityUid GenerateEntityUid() /// Generates a unique network id and increments /// protected virtual NetEntity GenerateNetEntity() => new(NextNetworkId++); + + [Conditional("DEBUG")] + protected void ThreadCheck() + { +#if DEBUG + DebugTools.Assert( + Environment.CurrentManagedThreadId == _mainThreadId, + "Environment.CurrentManagedThreadId == _mainThreadId"); +#endif + } } public enum EntityMessageType : byte diff --git a/Robust.Shared/GameObjects/EntitySystemMessages/EntityRenamedEvent.cs b/Robust.Shared/GameObjects/EntitySystemMessages/EntityRenamedEvent.cs index 798359b6b35..cbb29dafc80 100644 --- a/Robust.Shared/GameObjects/EntitySystemMessages/EntityRenamedEvent.cs +++ b/Robust.Shared/GameObjects/EntitySystemMessages/EntityRenamedEvent.cs @@ -2,6 +2,7 @@ namespace Robust.Shared.GameObjects; /// /// Raised directed on an entity when its name is changed. +/// Contains the EntityUid as systems may need to subscribe to it without targeting a specific component. /// [ByRefEvent] -public readonly record struct EntityRenamedEvent(string NewName); +public readonly record struct EntityRenamedEvent(EntityUid Uid, string OldName, string NewName); diff --git a/Robust.Shared/GameObjects/IEntityManager.Components.cs b/Robust.Shared/GameObjects/IEntityManager.Components.cs index c371a3a055d..0a749eb4010 100644 --- a/Robust.Shared/GameObjects/IEntityManager.Components.cs +++ b/Robust.Shared/GameObjects/IEntityManager.Components.cs @@ -22,11 +22,13 @@ public partial interface IEntityManager /// /// Calls Initialize() on all registered components of the entity. /// + [Obsolete("Use InitializeEntity")] void InitializeComponents(EntityUid uid, MetaDataComponent? meta = null); /// /// Calls Startup() on all registered components of the entity. /// + [Obsolete("Use StartEntity")] void StartComponents(EntityUid uid); /// diff --git a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs index e9051c87c82..f332a94c54c 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs @@ -252,7 +252,7 @@ private bool AnyEntitiesIntersecting(MapId mapId, var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, uid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - if (state.Lookup.AnyEntitiesIntersecting(uid, state.Shape, localAabb, state.Transform, state.Flags, ignored: state.Ignored)) + if (state.Lookup.AnyEntitiesIntersecting(uid, state.Shape, localAabb, localTransform, state.Flags, ignored: state.Ignored)) { state.Found = true; return false; @@ -266,7 +266,7 @@ private bool AnyEntitiesIntersecting(MapId mapId, var mapUid = _map.GetMapOrInvalid(mapId); var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, mapUid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - state.Found = AnyEntitiesIntersecting(mapUid, shape, localAabb, shapeTransform, flags, ignored); + state.Found = AnyEntitiesIntersecting(mapUid, shape, localAabb, localTransform, flags, ignored); } return state.Found; @@ -454,26 +454,8 @@ public bool AnyEntitiesIntersecting(MapId mapId, Box2 worldAABB, LookupFlags fla { if (mapId == MapId.Nullspace) return false; - // Don't need to check contained entities as they have the same bounds as the parent. - var found = false; - - var state = (this, worldAABB, flags, found); - - _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, - static (EntityUid uid, MapGridComponent _, ref (EntityLookupSystem lookup, Box2 worldAABB, LookupFlags flags, bool found) tuple) => - { - if (!tuple.lookup.AnyLocalEntitiesIntersecting(uid, tuple.worldAABB, tuple.flags)) - return true; - - tuple.found = true; - return false; - }, approx: true, includeMap: false); - - if (state.found) - return true; - - var mapUid = _map.GetMapOrInvalid(mapId); - return AnyLocalEntitiesIntersecting(mapUid, worldAABB, flags); + var shape = new Polygon(worldAABB); + return AnyEntitiesIntersecting(mapId, shape, Physics.Transform.Empty, flags); } public HashSet GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) @@ -487,23 +469,8 @@ public void GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, HashSet intersecting, - Box2 worldAABB, SharedTransformSystem xformSystem, LookupFlags flags) tuple) => - { - var localAABB = tuple.xformSystem.GetInvWorldMatrix(gridUid).TransformBox(tuple.worldAABB); - tuple.lookup.AddLocalEntitiesIntersecting(gridUid, tuple.intersecting, localAABB, tuple.flags); - return true; - }, approx: true, includeMap: false); - - // Get map entities - var mapUid = _map.GetMapOrInvalid(mapId); - AddLocalEntitiesIntersecting(mapUid, intersecting, worldAABB, flags); - AddContained(intersecting, flags); + var shape = new Polygon(worldAABB); + AddEntitiesIntersecting(mapId, intersecting, shape, Physics.Transform.Empty, flags); } #endregion @@ -513,73 +480,15 @@ public void GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, HashSet - { - if (tuple.lookup.AnyEntitiesIntersecting(uid, tuple.worldBounds, tuple.flags)) - { - tuple.found = true; - return false; - } - return true; - }, approx: true, includeMap: false); - - if (state.found) - return true; - - var mapUid = _map.GetMapOrInvalid(mapId); - return AnyEntitiesIntersecting(mapUid, worldBounds, flags); + var shape = new Polygon(worldBounds); + return AnyEntitiesIntersecting(mapId, shape, Physics.Transform.Empty, flags); } public HashSet GetEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags) { var intersecting = new HashSet(); - - if (mapId == MapId.Nullspace) - return intersecting; - - var mapUid = _map.GetMapOrInvalid(mapId); - - // Get grid entities var shape = new Polygon(worldBounds); - var transform = Physics.Transform.Empty; - - var state = (this, _physics, intersecting, transform, shape, flags); - - _mapManager.FindGridsIntersecting(mapUid, shape, transform, ref state, - static ( - EntityUid uid, - MapGridComponent grid, - ref (EntityLookupSystem lookup, - SharedPhysicsSystem _physics, - HashSet intersecting, - Transform transform, - Polygon shape, LookupFlags flags) state) => - { - var localTransform = state._physics.GetRelativePhysicsTransform(state.transform, uid); - var localAabb = state.shape.ComputeAABB(localTransform, 0); - - state.lookup.AddEntitiesIntersecting(uid, - state.intersecting, - state.shape, - localAabb, - state.transform, - state.flags); - return true; - }); - - // Get map entities - var localTransform = _physics.GetRelativePhysicsTransform(transform, mapUid); - var localAabb = shape.ComputeAABB(localTransform, 0); - - AddEntitiesIntersecting(mapUid, intersecting, shape, localAabb, transform, flags); - AddContained(intersecting, flags); - + AddEntitiesIntersecting(mapId, intersecting, shape, Physics.Transform.Empty, flags); return intersecting; } @@ -599,47 +508,8 @@ public bool AnyEntitiesInRange(EntityUid uid, float range, LookupFlags flags = D if (mapPos.MapId == MapId.Nullspace) return false; - var rangeVec = new Vector2(range, range); - var worldAABB = new Box2(mapPos.Position - rangeVec, mapPos.Position + rangeVec); - var circle = new PhysShapeCircle(range, mapPos.Position); - - const bool found = false; - var transform = Physics.Transform.Empty; - var state = (this, _physics, transform, circle, flags, found, uid); - - _mapManager.FindGridsIntersecting(mapPos.MapId, worldAABB, ref state, static ( - EntityUid gridUid, - MapGridComponent _, ref ( - EntityLookupSystem lookup, - SharedPhysicsSystem physics, - Transform worldTransform, - PhysShapeCircle circle, - LookupFlags flags, - bool found, - EntityUid ignored) tuple) => - { - var localTransform = tuple.physics.GetRelativePhysicsTransform(tuple.worldTransform, gridUid); - var localAabb = tuple.circle.ComputeAABB(localTransform, 0); - - if (tuple.lookup.AnyEntitiesIntersecting(gridUid, tuple.circle, localAabb, localTransform, tuple.flags, tuple.ignored)) - { - tuple.found = true; - return false; - } - - return true; - }, approx: true, includeMap: false); - - if (state.found) - { - return true; - } - - var mapUid = _map.GetMapOrInvalid(mapPos.MapId); - var localTransform = _physics.GetRelativePhysicsTransform(transform, uid); - var localAabb = circle.ComputeAABB(localTransform, 0); - - return AnyEntitiesIntersecting(mapUid, circle, localAabb, localTransform, flags, uid); + var shape = new PhysShapeCircle(range, mapPos.Position); + return AnyEntitiesIntersecting(mapPos.MapId, shape, Physics.Transform.Empty, flags, uid); } public HashSet GetEntitiesInRange(EntityUid uid, float range, LookupFlags flags = DefaultFlags) diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs index 52e6134ff14..6b001b024e3 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs @@ -542,7 +542,7 @@ public void GetEntitiesIntersecting(Type type, MapId mapId, IPhysShape shape, Tr { var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, uid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, localAabb, state.Transform, state.Flags, state.Query); + state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, localAabb, localTransform, state.Flags, state.Query); return true; }, approx: true, includeMap: false); @@ -550,7 +550,7 @@ public void GetEntitiesIntersecting(Type type, MapId mapId, IPhysShape shape, Tr var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, mapUid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - AddEntitiesIntersecting(mapUid, intersecting, shape, localAabb, shapeTransform, flags, query); + AddEntitiesIntersecting(mapUid, intersecting, shape, localAabb, localTransform, flags, query); AddContained(intersecting, flags, query); } @@ -586,7 +586,7 @@ public void GetEntitiesIntersecting(MapId mapId, IPhysShape shape, Transform { var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, uid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, localAabb, state.Transform, state.Flags, state.Query); + state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, localAabb, localTransform, state.Flags, state.Query); return true; }, approx: true, includeMap: false); @@ -595,7 +595,7 @@ public void GetEntitiesIntersecting(MapId mapId, IPhysShape shape, Transform var localTransform = state.Physics.GetRelativePhysicsTransform(state.Transform, mapUid); var localAabb = state.Shape.ComputeAABB(localTransform, 0); - AddEntitiesIntersecting(mapUid, entities, shape, localAabb, shapeTransform, flags, query); + AddEntitiesIntersecting(mapUid, entities, shape, localAabb, localTransform, flags, query); AddContained(entities, flags, query); } } @@ -678,8 +678,8 @@ public HashSet GetComponentsInRange(MapId mapId, Vector2 worldPos, float r public void GetEntitiesInRange(MapId mapId, Vector2 worldPos, float range, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent { - var shape = new PhysShapeCircle(range); - var transform = new Transform(worldPos, 0f); + var shape = new PhysShapeCircle(range, worldPos); + var transform = Physics.Transform.Empty; GetEntitiesInRange(mapId, shape, transform, entities, flags); } diff --git a/Robust.Shared/GameObjects/Systems/MetaDataSystem.cs b/Robust.Shared/GameObjects/Systems/MetaDataSystem.cs index a67ba3d2589..2ee1e89e91f 100644 --- a/Robust.Shared/GameObjects/Systems/MetaDataSystem.cs +++ b/Robust.Shared/GameObjects/Systems/MetaDataSystem.cs @@ -47,12 +47,14 @@ public void SetEntityName(EntityUid uid, string value, MetaDataComponent? metada if (!_metaQuery.Resolve(uid, ref metadata) || value.Equals(metadata.EntityName)) return; + var oldName = metadata.EntityName; + metadata._entityName = value; if (raiseEvents) { - var ev = new EntityRenamedEvent(value); - RaiseLocalEvent(uid, ref ev); + var ev = new EntityRenamedEvent(uid, oldName, value); + RaiseLocalEvent(uid, ref ev, true); } Dirty(uid, metadata, metadata); diff --git a/Robust.Shared/GameObjects/Systems/SharedAppearanceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedAppearanceSystem.cs index 38ea710beac..55905a26df6 100644 --- a/Robust.Shared/GameObjects/Systems/SharedAppearanceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedAppearanceSystem.cs @@ -115,6 +115,32 @@ public void CopyData(Entity src, Entity + /// Appends appearance data from src to dest. If a key/value pair already exists in dest, it gets replaced. + /// If src has no nothing is done. + /// If dest has no AppearanceComponent then it is created. + /// + public void AppendData(Entity src, Entity dest) + { + if (!Resolve(src, ref src.Comp, false)) + return; + + AppendData(src.Comp, dest); + } + + public void AppendData(AppearanceComponent srcComp, Entity dest) + { + dest.Comp ??= EnsureComp(dest); + + foreach (var (key, value) in srcComp.AppearanceData) + { + dest.Comp.AppearanceData[key] = value; + } + + Dirty(dest, dest.Comp); + QueueUpdate(dest, dest.Comp); + } } [Serializable, NetSerializable] diff --git a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs index a60f78c84c1..0de5870a86c 100644 --- a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs @@ -94,9 +94,9 @@ internal void RegenerateCollision( { UpdateFixture(uid, chunk, rectangles, body, manager, xform); - foreach (var (id, fixture) in chunk.Fixtures) + foreach (var id in chunk.Fixtures) { - fixtures[id] = fixture; + fixtures[id] = manager.Fixtures[id]; } } @@ -157,8 +157,9 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles // Check if we even need to issue an eventbus event var updated = false; - foreach (var (oldId, oldFixture) in chunk.Fixtures) + foreach (var oldId in chunk.Fixtures) { + var oldFixture = manager.Fixtures[oldId]; var existing = false; // Handle deleted / updated fixtures @@ -196,16 +197,16 @@ private bool UpdateFixture(EntityUid uid, MapChunk chunk, List rectangles // Anything remaining is a new fixture (or at least, may have not serialized onto the chunk yet). foreach (var (id, fixture) in newFixtures.Span) { + chunk.Fixtures.Add(id); var existingFixture = _fixtures.GetFixtureOrNull(uid, id, manager: manager); // Check if it's the same (otherwise remove anyway). if (existingFixture?.Shape is PolygonShape poly && poly.EqualsApprox((PolygonShape) fixture.Shape)) { - chunk.Fixtures.Add(id, existingFixture); + continue; } - chunk.Fixtures.Add(id, fixture); _fixtures.CreateFixture(uid, id, fixture, false, manager, body, xform); } diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs index 929b904ace3..47a750f05f7 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs @@ -12,6 +12,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Dynamics; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -245,6 +246,7 @@ protected virtual void UpdatePvsChunks(Entity modifiedChunks; + switch (args.Current) { case MapGridComponentDeltaState delta: @@ -257,9 +259,9 @@ private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref Co if (delta.ChunkData == null) return; - foreach (var chunkData in delta.ChunkData) + foreach (var (index, chunkData) in delta.ChunkData) { - ApplyChunkData(uid, component, chunkData, modifiedChunks); + ApplyChunkData(uid, component, index, chunkData, modifiedChunks); } component.LastTileModifiedTick = delta.LastTileModifiedTick; @@ -277,12 +279,12 @@ private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref Co foreach (var index in component.Chunks.Keys) { if (!state.FullGridData.ContainsKey(index)) - ApplyChunkData(uid, component, ChunkDatum.CreateDeleted(index), modifiedChunks); + ApplyChunkData(uid, component, index, ChunkDatum.Empty, modifiedChunks); } - foreach (var (index, tiles) in state.FullGridData) + foreach (var (index, data) in state.FullGridData) { - ApplyChunkData(uid, component, ChunkDatum.CreateModified(index, tiles), modifiedChunks); + ApplyChunkData(uid, component, index, new(data), modifiedChunks); } break; @@ -291,12 +293,8 @@ private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref Co return; } - var count = component.Chunks.Count; - RegenerateCollision(uid, component, modifiedChunks); - - // Regeneration can remove chunks in general, but it shouldn't do that here as the state handling - // should already have removed all the chunks. - DebugTools.AssertEqual(component.Chunks.Count, count); + RegenerateAabb(component); + OnGridBoundsChange(uid, component); #if DEBUG foreach (var chunk in component.Chunks.Values) @@ -307,7 +305,11 @@ private void OnGridHandleState(EntityUid uid, MapGridComponent component, ref Co #endif } - private void ApplyChunkData(EntityUid uid, MapGridComponent component, ChunkDatum data, + private void ApplyChunkData( + EntityUid uid, + MapGridComponent component, + Vector2i index, + ChunkDatum data, HashSet modifiedChunks) { bool shapeChanged = false; @@ -315,7 +317,7 @@ private void ApplyChunkData(EntityUid uid, MapGridComponent component, ChunkDatu if (data.IsDeleted()) { - if (!component.Chunks.TryGetValue(data.Index, out var deletedChunk)) + if (!component.Chunks.TryGetValue(index, out var deletedChunk)) return; // Deleted chunks still need to raise tile-changed events. @@ -329,18 +331,18 @@ private void ApplyChunkData(EntityUid uid, MapGridComponent component, ChunkDatu var gridIndices = deletedChunk.ChunkTileToGridTile((x, y)); var newTileRef = new TileRef(uid, gridIndices, Tile.Empty); - _mapInternal.RaiseOnTileChanged(newTileRef, oldTile, data.Index); + _mapInternal.RaiseOnTileChanged(newTileRef, oldTile, index); } } - component.Chunks.Remove(data.Index); + component.Chunks.Remove(index); // TODO is this required? modifiedChunks.Add(deletedChunk); return; } - var chunk = GetOrAddChunk(uid, component, data.Index); + var chunk = GetOrAddChunk(uid, component, index); chunk.SuppressCollisionRegeneration = true; DebugTools.Assert(data.TileData.Any(x => !x.IsEmpty)); DebugTools.Assert(data.TileData.Length == component.ChunkSize * component.ChunkSize); @@ -355,13 +357,24 @@ private void ApplyChunkData(EntityUid uid, MapGridComponent component, ChunkDatu shapeChanged |= tileShapeChanged; var gridIndices = chunk.ChunkTileToGridTile((x, y)); var newTileRef = new TileRef(uid, gridIndices, tile); - _mapInternal.RaiseOnTileChanged(newTileRef, oldTile, data.Index); + _mapInternal.RaiseOnTileChanged(newTileRef, oldTile, index); } } + if (data.Fixtures != null && !chunk.Fixtures.SetEquals(data.Fixtures)) + { + chunk.Fixtures.Clear(); + + if (data.Fixtures != null) + chunk.Fixtures.UnionWith(data.Fixtures); + } + + chunk.CachedBounds = data.CachedBounds!.Value; chunk.SuppressCollisionRegeneration = false; if (shapeChanged) + { modifiedChunks.Add(chunk); + } } private void OnGridGetState(EntityUid uid, MapGridComponent component, ref ComponentGetState args) @@ -372,7 +385,7 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo return; } - List? chunkData; + Dictionary? chunkData; var fromTick = args.FromTick; if (component.LastTileModifiedTick < fromTick) @@ -381,7 +394,7 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo } else { - chunkData = new List(); + chunkData = new Dictionary(); foreach (var (tick, indices) in component.ChunkDeletionHistory) { @@ -391,7 +404,7 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo // Chunk may have been re-added sometime after it was deleted, but before deletion history was culled. if (!component.Chunks.TryGetValue(indices, out var chunk)) { - chunkData.Add(ChunkDatum.CreateDeleted(indices)); + chunkData.Add(indices, ChunkDatum.Empty); continue; } @@ -416,7 +429,7 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo tileBuffer[x * component.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y); } } - chunkData.Add(ChunkDatum.CreateModified(index, tileBuffer)); + chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, chunk.Fixtures, chunk.CachedBounds)); } } @@ -427,12 +440,12 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo return; HashSet keys = new(); - foreach (var chunk in chunkData) + foreach (var (index, chunk) in chunkData) { - if (chunk.TileData == null) + if (chunk.IsDeleted()) continue; - DebugTools.Assert(keys.Add(chunk.Index), "Duplicate chunk"); + DebugTools.Assert(keys.Add(index), "Duplicate chunk"); DebugTools.Assert(chunk.TileData.Any(x => !x.IsEmpty), "Empty non-deleted chunk"); } #endif @@ -440,7 +453,7 @@ private void OnGridGetState(EntityUid uid, MapGridComponent component, ref Compo private void GetFullState(EntityUid uid, MapGridComponent component, ref ComponentGetState args) { - var chunkData = new Dictionary(); + var chunkData = new Dictionary(); foreach (var (index, chunk) in GetMapChunks(uid, component)) { @@ -453,7 +466,7 @@ private void GetFullState(EntityUid uid, MapGridComponent component, ref Compone tileBuffer[x * component.ChunkSize + y] = chunk.GetTile((ushort)x, (ushort)y); } } - chunkData.Add(index, tileBuffer); + chunkData.Add(index, ChunkDatum.CreateModified(tileBuffer, chunk.Fixtures, chunk.CachedBounds)); } args.State = new MapGridComponentState(component.ChunkSize, chunkData, component.LastTileModifiedTick); @@ -461,7 +474,7 @@ private void GetFullState(EntityUid uid, MapGridComponent component, ref Compone #if DEBUG foreach (var chunk in chunkData.Values) { - DebugTools.Assert(chunk.Any(x => !x.IsEmpty)); + DebugTools.Assert(chunk.TileData!.Any(x => !x.IsEmpty)); } #endif } @@ -495,7 +508,7 @@ private void OnGridInit(EntityUid uid, MapGridComponent component, ComponentInit if (TryComp(xform.MapUid, out var gridTree)) { - var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, component)); + var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _fixturesQuery.Comp(uid), component)); DebugTools.Assert(component.MapProxy == DynamicTree.Proxy.Free); component.MapProxy = proxy; } @@ -549,7 +562,7 @@ private void AddGrid(EntityUid uid, MapGridComponent grid) if (TryComp(xform.MapUid, out var gridTree)) { - var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, grid)); + var proxy = gridTree.Tree.CreateProxy(in aabb, (uid, _fixturesQuery.Comp(uid), grid)); DebugTools.Assert(grid.MapProxy == DynamicTree.Proxy.Free); grid.MapProxy = proxy; } @@ -629,9 +642,9 @@ internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnl PhysicsComponent? body = null; TransformComponent? xform = null; - foreach (var (id, fixture) in mapChunk.Fixtures) + foreach (var id in mapChunk.Fixtures) { - _fixtures.DestroyFixture(uid, id, fixture, false, manager: manager, body: body, xform: xform); + _fixtures.DestroyFixture(uid, id, false, manager: manager, body: body, xform: xform); } RemoveChunk(uid, grid, mapChunk.Indices); @@ -639,6 +652,20 @@ internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnl } } + RegenerateAabb(grid); + + // May have been deleted from the bulk update above! + if (Deleted(uid)) + return; + + _physics.WakeBody(uid); + OnGridBoundsChange(uid, grid); + var ev = new RegenerateGridBoundsEvent(uid, chunkRectangles, removedChunks); + RaiseLocalEvent(ref ev); + } + + private void RegenerateAabb(MapGridComponent grid) + { grid.LocalAABB = new Box2(); foreach (var chunk in grid.Chunks.Values) @@ -659,15 +686,6 @@ internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnl grid.LocalAABB = grid.LocalAABB.Union(gridBounds); } } - - // May have been deleted from the bulk update above! - if (Deleted(uid)) - return; - - _physics.WakeBody(uid); - OnGridBoundsChange(uid, grid); - var ev = new RegenerateGridBoundsEvent(uid, chunkRectangles, removedChunks); - RaiseLocalEvent(ref ev); } /// @@ -689,16 +707,31 @@ private void ClearEmptyMapChunks(EntityUid uid, MapGridComponent grid, IReadOnly #region TileAccess + public TileRef GetTileRef(Entity grid, MapCoordinates coords) + { + return GetTileRef(grid.Owner, grid.Comp, coords); + } + public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, MapCoordinates coords) { return GetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords)); } + public TileRef GetTileRef(Entity grid, EntityCoordinates coords) + { + return GetTileRef(grid.Owner, grid.Comp, coords); + } + public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, EntityCoordinates coords) { return GetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords)); } + public TileRef GetTileRef(Entity grid, Vector2i tileCoordinates) + { + return GetTileRef(grid.Owner, grid.Comp, tileCoordinates); + } + public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, Vector2i tileCoordinates) { var chunkIndices = GridTileToChunkIndices(uid, grid, tileCoordinates); @@ -1071,16 +1104,31 @@ public int AnchoredEntityCount(EntityUid uid, MapGridComponent grid, Vector2i po return chunk.GetSnapGrid((ushort)x, (ushort)y)?.Count ?? 0; // ? } + public IEnumerable GetAnchoredEntities(Entity grid, MapCoordinates coords) + { + return GetAnchoredEntities(grid.Owner, grid.Comp, coords); + } + public IEnumerable GetAnchoredEntities(EntityUid uid, MapGridComponent grid, MapCoordinates coords) { return GetAnchoredEntities(uid, grid, TileIndicesFor(uid, grid, coords)); } + public IEnumerable GetAnchoredEntities(Entity grid, EntityCoordinates coords) + { + return GetAnchoredEntities(grid.Owner, grid.Comp, coords); + } + public IEnumerable GetAnchoredEntities(EntityUid uid, MapGridComponent grid, EntityCoordinates coords) { return GetAnchoredEntities(uid, grid, TileIndicesFor(uid, grid, coords)); } + public IEnumerable GetAnchoredEntities(Entity grid, Vector2i pos) + { + return GetAnchoredEntities(grid.Owner, grid.Comp, pos); + } + public IEnumerable GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Vector2i pos) { // Because some content stuff checks neighboring tiles (which may not actually exist) we won't just @@ -1173,6 +1221,11 @@ public Vector2i TileIndicesFor(EntityUid uid, MapGridComponent grid, EntityCoord return SnapGridLocalCellFor(uid, grid, LocalToGrid(uid, grid, coords)); } + public Vector2i TileIndicesFor(Entity grid, EntityCoordinates coords) + { + return TileIndicesFor(grid.Owner, grid.Comp, coords); + } + public Vector2i TileIndicesFor(EntityUid uid, MapGridComponent grid, MapCoordinates worldPos) { #if DEBUG @@ -1184,6 +1237,11 @@ public Vector2i TileIndicesFor(EntityUid uid, MapGridComponent grid, MapCoordina return SnapGridLocalCellFor(uid, grid, localPos); } + public Vector2i TileIndicesFor(Entity grid, MapCoordinates coords) + { + return TileIndicesFor(grid.Owner, grid.Comp, coords); + } + private Vector2i SnapGridLocalCellFor(EntityUid uid, MapGridComponent grid, Vector2 localPos) { var x = (int)Math.Floor(localPos.X / grid.TileSize); diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs index b99177cc18f..f87a98e33cb 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.cs @@ -5,6 +5,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Network; +using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; @@ -23,6 +24,7 @@ public abstract partial class SharedMapSystem : EntitySystem [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly MetaDataSystem _meta = default!; + private EntityQuery _fixturesQuery; private EntityQuery _mapQuery; private EntityQuery _gridQuery; private EntityQuery _metaQuery; @@ -34,6 +36,7 @@ public override void Initialize() { base.Initialize(); + _fixturesQuery = GetEntityQuery(); _mapQuery = GetEntityQuery(); _gridQuery = GetEntityQuery(); _metaQuery = GetEntityQuery(); diff --git a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs index 130d160e4c9..aed80ff5cba 100644 --- a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs +++ b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs @@ -107,6 +107,11 @@ public bool AnchorEntity(EntityUid uid, TransformComponent xform, MapGridCompone return AnchorEntity(uid, xform, grid.Owner, grid, tileIndices); } + public bool AnchorEntity(EntityUid uid) + { + return AnchorEntity(uid, XformQuery.GetComponent(uid)); + } + public bool AnchorEntity(EntityUid uid, TransformComponent xform) { return AnchorEntity((uid, xform)); @@ -127,6 +132,11 @@ public bool AnchorEntity(Entity entity, Entity ent, ref Com // I.e., don't resend the whole BUI state just because a new user opened it. var actors = new Dictionary>(); - args.State = new UserInterfaceComponent.UserInterfaceComponentState(actors, ent.Comp.States, ent.Comp.Interfaces); + + var dataCopy = new Dictionary(); + + foreach (var (weh, a) in ent.Comp.Interfaces) + { + dataCopy[weh] = new InterfaceData(a); + } + + args.State = new UserInterfaceComponent.UserInterfaceComponentState(actors, ent.Comp.States, dataCopy); // Ensure that only the player that currently has the UI open gets to know what they have it open. if (args.ReplayState) @@ -335,11 +343,7 @@ private void OnUserInterfaceHandleState(Entity ent, ref foreach (var data in state.Data) { - ent.Comp.Interfaces[data.Key] = new(data.Value.ClientType) - { - InteractionRange = data.Value.InteractionRange, - RequireInputValidation = data.Value.RequireInputValidation, - }; + ent.Comp.Interfaces[data.Key] = new(data.Value); } foreach (var key in ent.Comp.Actors.Keys) diff --git a/Robust.Shared/GameStates/GameStateMapData.cs b/Robust.Shared/GameStates/GameStateMapData.cs index 3fb09a608d7..2daf838c77a 100644 --- a/Robust.Shared/GameStates/GameStateMapData.cs +++ b/Robust.Shared/GameStates/GameStateMapData.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -9,33 +10,49 @@ namespace Robust.Shared.GameStates [Serializable, NetSerializable] public readonly struct ChunkDatum { - public readonly Vector2i Index; + public static readonly ChunkDatum Empty = new ChunkDatum(); + + public readonly HashSet? Fixtures; // Definitely wasteful to send EVERY tile. // Optimize away future coder. // Also it's stored row-major. public readonly Tile[]? TileData; + public readonly Box2i? CachedBounds; + [MemberNotNullWhen(false, nameof(TileData))] public bool IsDeleted() { return TileData == null; } - private ChunkDatum(Vector2i index, Tile[] tileData) + internal ChunkDatum(ChunkDatum data) { - Index = index; - TileData = tileData; + if (data.TileData != null) + { + TileData = new Tile[data.TileData.Length]; + data.TileData.CopyTo(TileData, 0); + } + + if (data.Fixtures != null) + { + Fixtures = new HashSet(data.Fixtures); + } + + CachedBounds = data.CachedBounds; } - public static ChunkDatum CreateModified(Vector2i index, Tile[] tileData) + private ChunkDatum(Tile[] tileData, HashSet fixtures, Box2i cachedBounds) { - return new ChunkDatum(index, tileData); + TileData = tileData; + Fixtures = fixtures; + CachedBounds = cachedBounds; } - public static ChunkDatum CreateDeleted(Vector2i index) + public static ChunkDatum CreateModified(Tile[] tileData, HashSet fixtures, Box2i cachedBounds) { - return new ChunkDatum(index, null!); + return new ChunkDatum(tileData, fixtures, cachedBounds); } } } diff --git a/Robust.Shared/Map/Components/GridTreeComponent.cs b/Robust.Shared/Map/Components/GridTreeComponent.cs index 8e888938812..8a38b1b2b50 100644 --- a/Robust.Shared/Map/Components/GridTreeComponent.cs +++ b/Robust.Shared/Map/Components/GridTreeComponent.cs @@ -9,5 +9,5 @@ namespace Robust.Shared.Map.Components; public sealed partial class GridTreeComponent : Component { [ViewVariables] - public readonly B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> Tree = new(); + public readonly B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree = new(); } diff --git a/Robust.Shared/Map/Components/MapGridComponent.cs b/Robust.Shared/Map/Components/MapGridComponent.cs index 2ba82aaeeb2..882d6a821d1 100644 --- a/Robust.Shared/Map/Components/MapGridComponent.cs +++ b/Robust.Shared/Map/Components/MapGridComponent.cs @@ -335,7 +335,7 @@ public bool TryGetTileRef(EntityCoordinates coords, out TileRef tile) /// Serialized state of a . /// [Serializable, NetSerializable] - internal sealed class MapGridComponentState(ushort chunkSize, Dictionary fullGridData, GameTick lastTileModifiedTick) : ComponentState + internal sealed class MapGridComponentState(ushort chunkSize, Dictionary fullGridData, GameTick lastTileModifiedTick) : ComponentState { /// /// The size of the chunks in the map grid. @@ -345,7 +345,7 @@ internal sealed class MapGridComponentState(ushort chunkSize, Dictionary /// Networked chunk data containing the full grid state. /// - public Dictionary FullGridData = fullGridData; + public Dictionary FullGridData = fullGridData; /// /// Last game tick that the tile on the grid was modified. @@ -357,7 +357,7 @@ internal sealed class MapGridComponentState(ushort chunkSize, Dictionary. /// [Serializable, NetSerializable] - internal sealed class MapGridComponentDeltaState(ushort chunkSize, List? chunkData, GameTick lastTileModifiedTick) + internal sealed class MapGridComponentDeltaState(ushort chunkSize, Dictionary? chunkData, GameTick lastTileModifiedTick) : ComponentState, IComponentDeltaState { /// @@ -368,7 +368,7 @@ internal sealed class MapGridComponentDeltaState(ushort chunkSize, List /// Networked chunk data. /// - public readonly List? ChunkData = chunkData; + public readonly Dictionary? ChunkData = chunkData; /// /// Last game tick that the tile on the grid was modified. @@ -382,12 +382,12 @@ public void ApplyToFullState(MapGridComponentState state) if (ChunkData == null) return; - foreach (var data in ChunkData) + foreach (var (index, data) in ChunkData) { if (data.IsDeleted()) - state.FullGridData!.Remove(data.Index); + state.FullGridData.Remove(index); else - state.FullGridData![data.Index] = data.TileData; + state.FullGridData[index] = new(data); } state.LastTileModifiedTick = LastTileModifiedTick; @@ -395,12 +395,11 @@ public void ApplyToFullState(MapGridComponentState state) public MapGridComponentState CreateNewFullState(MapGridComponentState state) { - var fullGridData = new Dictionary(state.FullGridData.Count); + var fullGridData = new Dictionary(state.FullGridData.Count); foreach (var (key, value) in state.FullGridData) { - var arr = fullGridData[key] = new Tile[value.Length]; - Array.Copy(value, arr, value.Length); + fullGridData[key] = new(value); } var newState = new MapGridComponentState(ChunkSize, fullGridData, LastTileModifiedTick); diff --git a/Robust.Shared/Map/MapChunk.cs b/Robust.Shared/Map/MapChunk.cs index 98cc4d4d3be..8c685b62098 100644 --- a/Robust.Shared/Map/MapChunk.cs +++ b/Robust.Shared/Map/MapChunk.cs @@ -39,12 +39,11 @@ internal sealed class MapChunk /// /// Chunk-local AABB of this chunk. /// + [ViewVariables] public Box2i CachedBounds { get; set; } - /// - /// Physics fixtures that make up this grid chunk. - /// - public Dictionary Fixtures { get; } = new(); + [ViewVariables] + internal HashSet Fixtures = new(); /// /// The last game simulation tick that a tile on this chunk was modified. diff --git a/Robust.Shared/Map/MapManager.Queries.cs b/Robust.Shared/Map/MapManager.Queries.cs index 5d90e96d182..e61e572747c 100644 --- a/Robust.Shared/Map/MapManager.Queries.cs +++ b/Robust.Shared/Map/MapManager.Queries.cs @@ -18,14 +18,16 @@ private bool IsIntersecting( ChunkEnumerator enumerator, IPhysShape shape, Transform shapeTransform, - EntityUid gridUid) + Entity grid) { - var gridTransform = _physics.GetPhysicsTransform(gridUid); + var gridTransform = _physics.GetPhysicsTransform(grid); while (enumerator.MoveNext(out var chunk)) { - foreach (var fixture in chunk.Fixtures.Values) + foreach (var id in chunk.Fixtures) { + var fixture = grid.Comp.Fixtures[id]; + for (var j = 0; j < fixture.Shape.ChildCount; j++) { if (_manifolds.TestOverlap(shape, 0, fixture.Shape, j, shapeTransform, gridTransform)) @@ -169,7 +171,7 @@ private void FindGridsIntersecting(EntityUid mapEnt, IPhysShape shape, B if (!overlappingChunks.MoveNext(out _)) return true; } - else if (!state.MapManager.IsIntersecting(overlappingChunks, state.Shape, state.Transform, data.Uid)) + else if (!state.MapManager.IsIntersecting(overlappingChunks, state.Shape, state.Transform, (data.Uid, data.Fixtures))) { return true; } @@ -345,7 +347,7 @@ private readonly record struct GridQueryState( Box2 WorldAABB, IPhysShape Shape, Transform Transform, - B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> Tree, + B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, SharedMapSystem MapSystem, MapManager MapManager, SharedTransformSystem TransformSystem, @@ -357,7 +359,7 @@ private record struct GridQueryState( Box2 WorldAABB, IPhysShape Shape, Transform Transform, - B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> Tree, + B2DynamicTree<(EntityUid Uid, FixturesComponent Fixtures, MapGridComponent Grid)> Tree, SharedMapSystem MapSystem, MapManager MapManager, SharedTransformSystem TransformSystem, diff --git a/Robust.Shared/Physics/Shapes/Polygon.cs b/Robust.Shared/Physics/Shapes/Polygon.cs index 78fd9a55a4b..64f0f3bd77c 100644 --- a/Robust.Shared/Physics/Shapes/Polygon.cs +++ b/Robust.Shared/Physics/Shapes/Polygon.cs @@ -1,7 +1,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; using Robust.Shared.Maths; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Serialization.Manager.Attributes; @@ -197,28 +196,6 @@ public bool Equals(IPhysShape? other) return true; } - public bool Equals(PolygonShape? other) - { - if (ReferenceEquals(null, other)) - return false; - if (ReferenceEquals(this, other)) - return true; - - if (!Radius.Equals(other.Radius) || VertexCount != other.VertexCount) - return false; - - for (var i = 0; i < VertexCount; i++) - { - var vert = Vertices[i]; - var otherVert = other.Vertices[i]; - - if (!vert.Equals(otherVert)) - return false; - } - - return true; - } - public override int GetHashCode() { return HashCode.Combine(VertexCount, Vertices.AsSpan(0, VertexCount).ToArray(), Radius); diff --git a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs index c57b0fe1a30..18aee0396f3 100644 --- a/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs +++ b/Robust.Shared/Physics/Systems/SharedBroadphaseSystem.cs @@ -249,24 +249,36 @@ private void HandleGridCollisions( // TODO: Need to handle grids colliding with non-grid entities with the same layer // (nothing in SS14 does this yet). + var fixture = _fixturesQuery.Comp(gridUid); + var physics = _physicsQuery.Comp(gridUid); var transform = _physicsSystem.GetPhysicsTransform(gridUid); - var state = (gridUid, grid, transform, worldMatrix, invWorldMatrix, _map, _physicsSystem, _transform, _physicsQuery, _xformQuery); + var state = ( + new Entity(gridUid, fixture, grid, physics), + transform, + worldMatrix, + invWorldMatrix, + _map, + _physicsSystem, + _transform, + _fixturesQuery, + _physicsQuery, + _xformQuery); _mapManager.FindGridsIntersecting(mapId, aabb, ref state, static (EntityUid uid, MapGridComponent component, - ref (EntityUid gridUid, - MapGridComponent grid, + ref (Entity grid, Transform transform, Matrix3x2 worldMatrix, Matrix3x2 invWorldMatrix, SharedMapSystem _map, SharedPhysicsSystem _physicsSystem, SharedTransformSystem xformSystem, + EntityQuery fixturesQuery, EntityQuery physicsQuery, EntityQuery xformQuery) tuple) => { - if (tuple.gridUid == uid || + if (tuple.grid.Owner == uid || !tuple.xformQuery.TryGetComponent(uid, out var collidingXform)) { return true; @@ -277,38 +289,43 @@ private void HandleGridCollisions( var otherTransform = tuple._physicsSystem.GetPhysicsTransform(uid); // Get Grid2 AABB in grid1 ref - var aabb1 = tuple.grid.LocalAABB.Intersect(tuple.invWorldMatrix.TransformBox(otherGridBounds)); + var aabb1 = tuple.grid.Comp2.LocalAABB.Intersect(tuple.invWorldMatrix.TransformBox(otherGridBounds)); // TODO: AddPair has a nasty check in there that's O(n) but that's also a general physics problem. - var ourChunks = tuple._map.GetLocalMapChunks(tuple.gridUid, tuple.grid, aabb1); - var physicsA = tuple.physicsQuery.GetComponent(tuple.gridUid); + var ourChunks = tuple._map.GetLocalMapChunks(tuple.grid.Owner, tuple.grid, aabb1); + var physicsA = tuple.grid.Comp3; var physicsB = tuple.physicsQuery.GetComponent(uid); + var fixturesB = tuple.fixturesQuery.Comp(uid); // Only care about chunks on other grid overlapping us. while (ourChunks.MoveNext(out var ourChunk)) { var ourChunkWorld = tuple.worldMatrix.TransformBox( - ourChunk.CachedBounds.Translated(ourChunk.Indices * tuple.grid.ChunkSize)); + ourChunk.CachedBounds.Translated(ourChunk.Indices * tuple.grid.Comp2.ChunkSize)); var ourChunkOtherRef = otherGridInvMatrix.TransformBox(ourChunkWorld); var collidingChunks = tuple._map.GetLocalMapChunks(uid, component, ourChunkOtherRef); while (collidingChunks.MoveNext(out var collidingChunk)) { - foreach (var (ourId, fixture) in ourChunk.Fixtures) + foreach (var ourId in ourChunk.Fixtures) { + var fixture = tuple.grid.Comp1.Fixtures[ourId]; + for (var i = 0; i < fixture.Shape.ChildCount; i++) { var fixAABB = fixture.Shape.ComputeAABB(tuple.transform, i); - foreach (var (otherId, otherFixture) in collidingChunk.Fixtures) + foreach (var otherId in collidingChunk.Fixtures) { + var otherFixture = fixturesB.Fixtures[otherId]; + for (var j = 0; j < otherFixture.Shape.ChildCount; j++) { var otherAABB = otherFixture.Shape.ComputeAABB(otherTransform, j); if (!fixAABB.Intersects(otherAABB)) continue; - tuple._physicsSystem.AddPair(tuple.gridUid, uid, + tuple._physicsSystem.AddPair(tuple.grid.Owner, uid, ourId, otherId, fixture, i, otherFixture, j, diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs index f5199867d13..907a03a4775 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Fixtures.cs @@ -81,7 +81,7 @@ public bool IsCurrentlyHardCollidable(Entity bodyA if (!_fixturesQuery.Resolve(bodyA, ref bodyA.Comp1, false) || !_fixturesQuery.Resolve(bodyB, ref bodyB.Comp1, false) || !PhysicsQuery.Resolve(bodyA, ref bodyA.Comp2, false) || - !PhysicsQuery.Resolve(bodyA, ref bodyB.Comp2, false)) + !PhysicsQuery.Resolve(bodyB, ref bodyB.Comp2, false)) { return false; } diff --git a/Robust.Shared/Serialization/Manager/Definition/DataDefinitionUtility.cs b/Robust.Shared/Serialization/Manager/Definition/DataDefinitionUtility.cs index 1a6adb89c58..72aec126e46 100644 --- a/Robust.Shared/Serialization/Manager/Definition/DataDefinitionUtility.cs +++ b/Robust.Shared/Serialization/Manager/Definition/DataDefinitionUtility.cs @@ -2,7 +2,7 @@ namespace Robust.Shared.Serialization.Manager.Definition; -public class DataDefinitionUtility +public static class DataDefinitionUtility { public static string AutoGenerateTag(string name) { diff --git a/Robust.Shared/Serialization/Manager/SerializationManager.Writing.cs b/Robust.Shared/Serialization/Manager/SerializationManager.Writing.cs index 40f2e577d5f..3bc5109dc28 100644 --- a/Robust.Shared/Serialization/Manager/SerializationManager.Writing.cs +++ b/Robust.Shared/Serialization/Manager/SerializationManager.Writing.cs @@ -260,7 +260,12 @@ public DataNode WriteValue(T value, bool alwaysWrite = false, ISerializationC return ValueDataNode.Null(); } - return GetOrCreateWriteGenericDelegate(value, notNullableOverride)(value, alwaysWrite, context); + var node = GetOrCreateWriteGenericDelegate(value, notNullableOverride)(value, alwaysWrite, context); + + if (typeof(T) == typeof(object)) + node.Tag = "!type:" + value.GetType().Name; + + return node; } public DataNode WriteValue(ITypeWriter writer, T value, bool alwaysWrite = false, diff --git a/Robust.Shared/Serialization/TypeSerializers/Implementations/Generic/ObjectSerializer.cs b/Robust.Shared/Serialization/TypeSerializers/Implementations/Generic/ObjectSerializer.cs new file mode 100644 index 00000000000..d9d93aca47d --- /dev/null +++ b/Robust.Shared/Serialization/TypeSerializers/Implementations/Generic/ObjectSerializer.cs @@ -0,0 +1,92 @@ +using System; +using Robust.Shared.Reflection; +using Robust.Shared.IoC; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.Markdown.Value; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; + +[TypeSerializer] +public sealed class ObjectSerializer : ITypeSerializer, ITypeCopier +{ + #region Validate + + public ValidationNode Validate(ISerializationManager serializationManager, ValueDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + var reflection = dependencies.Resolve(); + + if (node.Tag != null) + { + string? typeString = node.Tag[6..]; + + if (!reflection.TryLooseGetType(typeString, out var type)) + { + return new ErrorNode(node, $"Unable to find type for {typeString}"); + } + + return serializationManager.ValidateNode(type, node, context); + } + return new ErrorNode(node, $"Unable to find type for {node}"); + } + + #endregion + + #region Read + public object Read(ISerializationManager serializationManager, ValueDataNode node, + IDependencyCollection dependencies, + SerializationHookContext hookCtx, ISerializationContext? context = null, + ISerializationManager.InstantiationDelegate? instanceProvider = null) + { + var reflection = dependencies.Resolve(); + var value = instanceProvider != null ? instanceProvider() : new object(); + + if (node.Tag != null) + { + string? typeString = node.Tag[6..]; + + if (!reflection.TryLooseGetType(typeString, out var type)) + throw new NullReferenceException($"Found null type for {typeString}"); + + value = serializationManager.Read(type, node, hookCtx, context); + + if (value == null) + throw new NullReferenceException($"Found null data for {node}, expected {type}"); + } + + return value; + } + #endregion + + #region Write + public DataNode Write(ISerializationManager serializationManager, object value, + IDependencyCollection dependencies, bool alwaysWrite = false, + ISerializationContext? context = null) + { + var node = serializationManager.WriteValue(value.GetType(), value); + + if (node == null) + throw new NullReferenceException($"Attempted to write node with type {value.GetType()}, node returned null"); + + return node; + } + #endregion + + #region CopyTo + + public void CopyTo( + ISerializationManager serializationManager, + object source, + ref object target, + IDependencyCollection dependencies, + SerializationHookContext hookCtx, + ISerializationContext? context = null) + { + target = source; + } + #endregion +} diff --git a/Robust.Shared/Toolshed/Syntax/Block.cs b/Robust.Shared/Toolshed/Syntax/Block.cs index eeb2c04d4c0..b6d3264a045 100644 --- a/Robust.Shared/Toolshed/Syntax/Block.cs +++ b/Robust.Shared/Toolshed/Syntax/Block.cs @@ -134,7 +134,7 @@ public record struct MissingClosingBrace() : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup("Expected a closing brace."); + return FormattedMessage.FromUnformatted("Expected a closing brace."); } public string? Expression { get; set; } diff --git a/Robust.Shared/Toolshed/Syntax/Expression.cs b/Robust.Shared/Toolshed/Syntax/Expression.cs index 8472ddfd4b8..5e603a8c073 100644 --- a/Robust.Shared/Toolshed/Syntax/Expression.cs +++ b/Robust.Shared/Toolshed/Syntax/Expression.cs @@ -182,7 +182,7 @@ public record struct ExpressionOfWrongType(Type Expected, Type Got, bool Once) : { public FormattedMessage DescribeInner() { - var msg = FormattedMessage.FromMarkup( + var msg = FormattedMessage.FromUnformatted( $"Expected an expression of type {Expected.PrettyName()}, but got {Got.PrettyName()}"); if (Once) diff --git a/Robust.Shared/Toolshed/Syntax/ParsedCommand.cs b/Robust.Shared/Toolshed/Syntax/ParsedCommand.cs index c4b695132ee..c061fe62de0 100644 --- a/Robust.Shared/Toolshed/Syntax/ParsedCommand.cs +++ b/Robust.Shared/Toolshed/Syntax/ParsedCommand.cs @@ -240,7 +240,7 @@ public record struct UnknownCommandError(string Cmd) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup($"Got unknown command {Cmd}."); + return FormattedMessage.FromUnformatted($"Got unknown command {Cmd}."); } public string? Expression { get; set; } @@ -252,7 +252,7 @@ public record NoImplementationError(string Cmd, Type[] Types, string? SubCommand { public FormattedMessage DescribeInner() { - var msg = FormattedMessage.FromMarkup($"Could not find an implementation for {Cmd} given the input type {PipedType?.PrettyName() ?? "void"}."); + var msg = FormattedMessage.FromUnformatted($"Could not find an implementation for {Cmd} given the input type {PipedType?.PrettyName() ?? "void"}."); msg.PushNewline(); var typeArgs = ""; diff --git a/Robust.Shared/Toolshed/ToolshedEnvironment.cs b/Robust.Shared/Toolshed/ToolshedEnvironment.cs index 35a8c0da1e4..b1f998f9001 100644 --- a/Robust.Shared/Toolshed/ToolshedEnvironment.cs +++ b/Robust.Shared/Toolshed/ToolshedEnvironment.cs @@ -14,7 +14,6 @@ public sealed class ToolshedEnvironment { [Dependency] private readonly IReflectionManager _reflection = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ToolshedManager _toolshedManager = default!; private readonly Dictionary _commands = new(); private readonly Dictionary> _commandPipeValueMap = new(); diff --git a/Robust.Shared/Toolshed/ToolshedManager.Parsing.cs b/Robust.Shared/Toolshed/ToolshedManager.Parsing.cs index 78192b55e4d..b346c99a8f9 100644 --- a/Robust.Shared/Toolshed/ToolshedManager.Parsing.cs +++ b/Robust.Shared/Toolshed/ToolshedManager.Parsing.cs @@ -181,17 +181,17 @@ public FormattedMessage DescribeInner() if (T.Constructable()) { - var msg = FormattedMessage.FromMarkup( + var msg = FormattedMessage.FromUnformatted( $"The type {T.PrettyName()} has no parser available and cannot be parsed."); msg.PushNewline(); msg.AddText("Please contact a programmer with this error, they'd probably like to see it."); msg.PushNewline(); - msg.AddMarkup("[bold][color=red]THIS IS A BUG.[/color][/bold]"); + msg.AddMarkupOrThrow("[bold][color=red]THIS IS A BUG.[/color][/bold]"); return msg; } else { - return FormattedMessage.FromMarkup($"The type {T.PrettyName()} cannot be parsed, as it cannot be constructed."); + return FormattedMessage.FromUnformatted($"The type {T.PrettyName()} cannot be parsed, as it cannot be constructed."); } } diff --git a/Robust.Shared/Toolshed/TypeParsers/BoolTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/BoolTypeParser.cs index f5aa55f7d01..5a929af08e9 100644 --- a/Robust.Shared/Toolshed/TypeParsers/BoolTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/BoolTypeParser.cs @@ -60,7 +60,7 @@ public record InvalidBool(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup( + return FormattedMessage.FromUnformatted( $"The value {Value} is not a valid boolean."); } diff --git a/Robust.Shared/Toolshed/TypeParsers/ComponentTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/ComponentTypeParser.cs index 8acde11156d..fd8cad9428c 100644 --- a/Robust.Shared/Toolshed/TypeParsers/ComponentTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/ComponentTypeParser.cs @@ -61,7 +61,7 @@ public record struct UnknownComponentError(string Component) : IConError { public FormattedMessage DescribeInner() { - var msg = FormattedMessage.FromMarkup( + var msg = FormattedMessage.FromUnformatted( $"Unknown component {Component}. For a list of all components, try types:components." ); if (Component.EndsWith("component", true, CultureInfo.InvariantCulture)) diff --git a/Robust.Shared/Toolshed/TypeParsers/EntityTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/EntityTypeParser.cs index d270e28ad00..e5b40e11073 100644 --- a/Robust.Shared/Toolshed/TypeParsers/EntityTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/EntityTypeParser.cs @@ -49,7 +49,7 @@ public record InvalidEntity(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup($"Couldn't parse {Value} as an Entity."); + return FormattedMessage.FromUnformatted($"Couldn't parse {Value} as an Entity."); } public string? Expression { get; set; } @@ -61,7 +61,7 @@ public record DeadEntity(EntityUid Entity) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup($"The entity {Entity} does not exist."); + return FormattedMessage.FromUnformatted($"The entity {Entity} does not exist."); } public string? Expression { get; set; } diff --git a/Robust.Shared/Toolshed/TypeParsers/EnumTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/EnumTypeParser.cs index 0b5e0029c67..4a416daaeec 100644 --- a/Robust.Shared/Toolshed/TypeParsers/EnumTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/EnumTypeParser.cs @@ -57,7 +57,7 @@ public record InvalidEnum(string Value) : IConError { public FormattedMessage DescribeInner() { - var msg = FormattedMessage.FromMarkup($"The value {Value} is not a valid {typeof(T).PrettyName()}."); + var msg = FormattedMessage.FromUnformatted($"The value {Value} is not a valid {typeof(T).PrettyName()}."); msg.AddText($"Valid values are: {string.Join(", ", Enum.GetNames())}"); return msg; } diff --git a/Robust.Shared/Toolshed/TypeParsers/Math/AngleTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/Math/AngleTypeParser.cs index 8885fff5bd8..f69839a5b1c 100644 --- a/Robust.Shared/Toolshed/TypeParsers/Math/AngleTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/Math/AngleTypeParser.cs @@ -70,7 +70,7 @@ public record InvalidAngle(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup( + return FormattedMessage.FromUnformatted( $"The value {Value} is not a valid angle."); } diff --git a/Robust.Shared/Toolshed/TypeParsers/Math/ColorTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/Math/ColorTypeParser.cs index 1e628d59dc0..7aac2c5e035 100644 --- a/Robust.Shared/Toolshed/TypeParsers/Math/ColorTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/Math/ColorTypeParser.cs @@ -60,7 +60,7 @@ public record InvalidColor(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup( + return FormattedMessage.FromUnformatted( $"The value {Value} is not a valid RGB color or name of color."); } diff --git a/Robust.Shared/Toolshed/TypeParsers/Math/NumberBaseTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/Math/NumberBaseTypeParser.cs index 57b0577caef..bac588db163 100644 --- a/Robust.Shared/Toolshed/TypeParsers/Math/NumberBaseTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/Math/NumberBaseTypeParser.cs @@ -56,7 +56,7 @@ public record InvalidNumber(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup( + return FormattedMessage.FromUnformatted( $"The value {Value} is not a valid {typeof(T).PrettyName()}."); } diff --git a/Robust.Shared/Toolshed/TypeParsers/Math/SpanLikeTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/Math/SpanLikeTypeParser.cs index 9f9340acc9e..34199ece3aa 100644 --- a/Robust.Shared/Toolshed/TypeParsers/Math/SpanLikeTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/Math/SpanLikeTypeParser.cs @@ -97,7 +97,7 @@ public record UnexpectedCloseBrace : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup("Unexpected closing brace."); + return FormattedMessage.FromUnformatted("Unexpected closing brace."); } public string? Expression { get; set; } diff --git a/Robust.Shared/Toolshed/TypeParsers/PrototypeTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/PrototypeTypeParser.cs index c8dcb62cdf7..56f28aae56c 100644 --- a/Robust.Shared/Toolshed/TypeParsers/PrototypeTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/PrototypeTypeParser.cs @@ -69,7 +69,7 @@ public record NotAValidPrototype(string Proto, string Kind) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup($"{Proto} is not a valid {Kind} prototype"); + return FormattedMessage.FromUnformatted($"{Proto} is not a valid {Kind} prototype"); } public string? Expression { get; set; } diff --git a/Robust.Shared/Toolshed/TypeParsers/QuantityTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/QuantityTypeParser.cs index f07033c2d2d..ac38a8345c0 100644 --- a/Robust.Shared/Toolshed/TypeParsers/QuantityTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/QuantityTypeParser.cs @@ -64,7 +64,7 @@ public record InvalidQuantity(string Value) : IConError { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup( + return FormattedMessage.FromUnformatted( $"The value {Value} is not a valid quantity. Please input some decimal number, optionally with a % to indicate that it's a percentage."); } diff --git a/Robust.Shared/Toolshed/TypeParsers/SessionTypeParser.cs b/Robust.Shared/Toolshed/TypeParsers/SessionTypeParser.cs index 69524e841dd..06f5d5a1954 100644 --- a/Robust.Shared/Toolshed/TypeParsers/SessionTypeParser.cs +++ b/Robust.Shared/Toolshed/TypeParsers/SessionTypeParser.cs @@ -54,7 +54,7 @@ public record InvalidUsername(ILocalizationManager Loc, string Username) : IConE { public FormattedMessage DescribeInner() { - return FormattedMessage.FromMarkup(Loc.GetString("cmd-parse-failure-session", ("username", Username))); + return FormattedMessage.FromUnformatted(Loc.GetString("cmd-parse-failure-session", ("username", Username))); } public string? Expression { get; set; } diff --git a/Robust.Shared/Utility/MarkupNode.cs b/Robust.Shared/Utility/MarkupNode.cs index fc6713bf486..9cf7b6706f1 100644 --- a/Robust.Shared/Utility/MarkupNode.cs +++ b/Robust.Shared/Utility/MarkupNode.cs @@ -7,7 +7,7 @@ namespace Robust.Shared.Utility; [Serializable, NetSerializable] -public sealed class MarkupNode : IComparable +public sealed class MarkupNode : IComparable, IEquatable { public readonly string? Name; public readonly MarkupParameter Value; @@ -43,7 +43,7 @@ public override string ToString() attributesString += $"{k}{v}"; } - return $"[{(Closing ? "/" : "")}{Name}{Value.ToString().ReplaceLineEndings("\\n") ?? ""}{attributesString}]"; + return $"[{(Closing ? "/" : "")}{Name}{Value.ToString().ReplaceLineEndings("\\n")}{attributesString}]"; } public override bool Equals(object? obj) @@ -51,14 +51,38 @@ public override bool Equals(object? obj) return obj is MarkupNode node && Equals(node); } - public bool Equals(MarkupNode node) + public bool Equals(MarkupNode? node) { - var equal = Name == node.Name; - equal &= Value.Equals(node.Value); - equal &= Attributes.Count == 0 && node.Attributes.Count == 0 || Attributes.Equals(node.Attributes); - equal &= Closing == node.Closing; + if (node is null) + return false; - return equal; + if (Name != node.Name) + return false; + + if (!Value.Equals(node.Value)) + return false; + + if (Closing != node.Closing) + return false; + + if (Attributes.Count != node.Attributes.Count) + return false; + + foreach (var (key, value) in Attributes) + { + if (!node.Attributes.TryGetValue(key, out var nodeValue)) + return false; + + if (!value.Equals(nodeValue)) + return false; + } + + return true; + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Value, Closing); } public int CompareTo(MarkupNode? other) @@ -69,7 +93,10 @@ public int CompareTo(MarkupNode? other) return 1; var nameComparison = string.Compare(Name, other.Name, StringComparison.Ordinal); - return nameComparison != 0 ? nameComparison : Closing.CompareTo(other.Closing); + if (nameComparison != 0) + return nameComparison; + + return Closing.CompareTo(other.Closing); } } diff --git a/Robust.UnitTesting/.editorconfig b/Robust.UnitTesting/.editorconfig new file mode 100644 index 00000000000..39f6478b7ed --- /dev/null +++ b/Robust.UnitTesting/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] +# Doing this via EditorConfig seems to be the only way to suppress in an entire project. +# This warning conflicts with NUnit's recommendation to subtype the "Is" type. +resharper_access_to_static_member_via_derived_type_highlighting=none diff --git a/Robust.UnitTesting/AssemblyInfo.cs b/Robust.UnitTesting/AssemblyInfo.cs index edba04417fb..fc728beff83 100644 --- a/Robust.UnitTesting/AssemblyInfo.cs +++ b/Robust.UnitTesting/AssemblyInfo.cs @@ -1,4 +1,7 @@ using System.Runtime.CompilerServices; +using NUnit.Framework; // So it can use RobustServerSimulation. [assembly: InternalsVisibleTo("Robust.Benchmarks")] + +[assembly: Parallelizable(ParallelScope.Fixtures)] diff --git a/Robust.UnitTesting/Client/Graphics/EyeTest.cs b/Robust.UnitTesting/Client/Graphics/EyeTest.cs index c1e70f31dab..a992c7286af 100644 --- a/Robust.UnitTesting/Client/Graphics/EyeTest.cs +++ b/Robust.UnitTesting/Client/Graphics/EyeTest.cs @@ -5,7 +5,6 @@ using Robust.Shared.Map; using Robust.Shared.Maths; -// ReSharper disable AccessToStaticMemberViaDerivedType namespace Robust.UnitTesting.Client.Graphics { [TestFixture, Parallelizable, TestOf(typeof(Eye))] diff --git a/Robust.UnitTesting/Client/UserInterface/ControlTest.cs b/Robust.UnitTesting/Client/UserInterface/ControlTest.cs index c0a1b585f0f..e0d4344a13a 100644 --- a/Robust.UnitTesting/Client/UserInterface/ControlTest.cs +++ b/Robust.UnitTesting/Client/UserInterface/ControlTest.cs @@ -84,7 +84,7 @@ public void TestVisibleInTree() control1.Visible = true; Assert.That(control2.VisibleInTree, Is.False); - control1.Dispose(); + control1.Orphan(); } [Test] diff --git a/Robust.UnitTesting/Client/UserInterface/UserInterfaceManagerTest.cs b/Robust.UnitTesting/Client/UserInterface/UserInterfaceManagerTest.cs index 512118f1f1f..ce5e891d6cb 100644 --- a/Robust.UnitTesting/Client/UserInterface/UserInterfaceManagerTest.cs +++ b/Robust.UnitTesting/Client/UserInterface/UserInterfaceManagerTest.cs @@ -133,10 +133,7 @@ void Control2MouseDown(GUIBoundKeyEventArgs ev) Assert.That(control3Fired, NUnit.Framework.Is.True); }); - control1.Dispose(); - control2.Dispose(); - control3.Dispose(); - control4.Dispose(); + control1.Orphan(); } [Test] @@ -144,7 +141,6 @@ public void TestGrabKeyboardFocus() { Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); var control1 = new Control {CanKeyboardFocus = true}; - var control2 = new Control {CanKeyboardFocus = true}; control1.GrabKeyboardFocus(); Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1)); @@ -152,9 +148,6 @@ public void TestGrabKeyboardFocus() control1.ReleaseKeyboardFocus(); Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); - - control1.Dispose(); - control2.Dispose(); } [Test] @@ -169,9 +162,6 @@ public void TestGrabKeyboardFocusSteal() Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control2)); control2.ReleaseKeyboardFocus(); Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); - - control1.Dispose(); - control2.Dispose(); } [Test] @@ -186,9 +176,6 @@ public void TestGrabKeyboardFocusOtherRelease() Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1)); _userInterfaceManager.ReleaseKeyboardFocus(); Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); - - control1.Dispose(); - control2.Dispose(); } [Test] @@ -226,7 +213,7 @@ public void TestGrabKeyboardFocusOnClick() _userInterfaceManager.ReleaseKeyboardFocus(); Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); - control.Dispose(); + control.Orphan(); } /// @@ -253,7 +240,7 @@ public void TestNotGrabKeyboardFocusOnClick() Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null); - control.Dispose(); + control.Orphan(); } } } diff --git a/Robust.UnitTesting/GameControllerDummy.cs b/Robust.UnitTesting/GameControllerDummy.cs index 1f9411614ee..a29ac8ae1a0 100644 --- a/Robust.UnitTesting/GameControllerDummy.cs +++ b/Robust.UnitTesting/GameControllerDummy.cs @@ -12,7 +12,7 @@ internal sealed class GameControllerDummy : IGameControllerInternal public GameControllerOptions Options { get; } = new(); public bool ContentStart { get; set; } - public event Action? TickUpdateOverride; + public event Action? TickUpdateOverride { add { } remove { } } public void Shutdown(string? reason = null) { diff --git a/Robust.UnitTesting/Server/GameObjects/Components/Container_Test.cs b/Robust.UnitTesting/Server/GameObjects/Components/Container_Test.cs index 80191f52cc3..de140421721 100644 --- a/Robust.UnitTesting/Server/GameObjects/Components/Container_Test.cs +++ b/Robust.UnitTesting/Server/GameObjects/Components/Container_Test.cs @@ -11,8 +11,6 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Server.GameObjects.Components { [TestFixture, Parallelizable] diff --git a/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs b/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs index ba1585e2ccb..a7accf49d1c 100644 --- a/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs +++ b/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs @@ -101,7 +101,7 @@ public void ParentMapSwitchTest() // move the parent, and the child should move with it XformSystem.SetLocalPosition(child, new Vector2(6, 6), childTrans); - XformSystem.SetWorldPosition(parentTrans, new Vector2(-8, -8)); + XformSystem.SetWorldPosition(parent, new Vector2(-8, -8)); Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(-2, -2))); @@ -131,8 +131,8 @@ public void ParentAttachMoveTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - XformSystem.SetWorldPosition(parentTrans, new Vector2(5, 5)); - XformSystem.SetWorldPosition(childTrans, new Vector2(6, 6)); + XformSystem.SetWorldPosition(parent, new Vector2(5, 5)); + XformSystem.SetWorldPosition(child, new Vector2(6, 6)); // Act var oldWpos = XformSystem.GetWorldPosition(childTrans); @@ -156,9 +156,9 @@ public void ParentDoubleAttachMoveTest() var parentTrans = EntityManager.GetComponent(parent); var childOneTrans = EntityManager.GetComponent(childOne); var childTwoTrans = EntityManager.GetComponent(childTwo); - XformSystem.SetWorldPosition(parentTrans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(childOneTrans, new Vector2(2, 2)); - XformSystem.SetWorldPosition(childTwoTrans, new Vector2(3, 3)); + XformSystem.SetWorldPosition(parent, new Vector2(1, 1)); + XformSystem.SetWorldPosition(childOne, new Vector2(2, 2)); + XformSystem.SetWorldPosition(childTwo, new Vector2(3, 3)); // Act var oldWpos = XformSystem.GetWorldPosition(childOneTrans); @@ -188,8 +188,8 @@ public void ParentRotateTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - XformSystem.SetWorldPosition(parentTrans, new Vector2(0, 0)); - XformSystem.SetWorldPosition(childTrans, new Vector2(2, 0)); + XformSystem.SetWorldPosition(parent, new Vector2(0, 0)); + XformSystem.SetWorldPosition(child, new Vector2(2, 0)); XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); //Act @@ -215,8 +215,8 @@ public void ParentTransRotateTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - XformSystem.SetWorldPosition(parentTrans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(childTrans, new Vector2(2, 1)); + XformSystem.SetWorldPosition(parent, new Vector2(1, 1)); + XformSystem.SetWorldPosition(child, new Vector2(2, 1)); XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); //Act @@ -248,10 +248,10 @@ public void PositionCompositionTest() var node3Trans = EntityManager.GetComponent(node3); var node4Trans = EntityManager.GetComponent(node4); - XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); - XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); - XformSystem.SetWorldPosition(node4Trans, new Vector2(0, 2)); + XformSystem.SetWorldPosition(node1, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node4, new Vector2(0, 2)); XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); @@ -285,9 +285,9 @@ public void ParentLocalPositionRoundingErrorTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); - XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node1, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3, new Vector2(2, 2)); XformSystem.SetParent(node1, node1Trans, node2, parentXform: node2Trans); XformSystem.SetParent(node2, node2Trans, node3, parentXform: node3Trans); @@ -330,9 +330,9 @@ public void ParentRotationRoundingErrorTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); - XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node1, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3, new Vector2(2, 2)); XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); @@ -379,10 +379,10 @@ public void TreeComposeWorldMatricesTest() var node3Trans = EntityManager.GetComponent(node3); var node4Trans = EntityManager.GetComponent(node4); - XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); - XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); - XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); - XformSystem.SetWorldPosition(node4Trans, new Vector2(0, 2)); + XformSystem.SetWorldPosition(node1, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node4, new Vector2(0, 2)); XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); @@ -390,7 +390,7 @@ public void TreeComposeWorldMatricesTest() //Act node1Trans.LocalRotation = new Angle(MathHelper.Pi / 6.37); - XformSystem.SetWorldPosition(node1Trans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node1, new Vector2(1, 1)); var worldMat = XformSystem.GetWorldMatrix(node4Trans); var invWorldMat = XformSystem.GetInvWorldMatrix(node4Trans); diff --git a/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs b/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs index f08e7c5630a..b8993f7a8f6 100644 --- a/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs +++ b/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs @@ -30,6 +30,7 @@ public async Task TestMultipleIndexChange() var confMan = server.ResolveDependency(); var sPlayerMan = server.ResolveDependency(); var xforms = sEntMan.System(); + var maps = sEntMan.System(); var cEntMan = client.ResolveDependency(); var netMan = client.ResolveDependency(); @@ -52,7 +53,7 @@ await server.WaitPost(() => { map = server.System().CreateMap(out var mapId); var gridComp = mapMan.CreateGridEntity(mapId); - gridComp.Comp.SetTile(Vector2i.Zero, new Tile(1)); + maps.SetTile(gridComp, Vector2i.Zero, new Tile(1)); grid = gridComp.Owner; }); diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index d8c71cbff23..55232b2e510 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -77,6 +77,32 @@ public interface ISimulation EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates); } + /// + /// Helper methods for working with . + /// + internal static class SimulationExtensions + { + public static T System(this ISimulation simulation) where T : IEntitySystem + { + return simulation.Resolve().GetEntitySystem(); + } + + public static bool HasComp(this ISimulation simulation, EntityUid entity) where T : IComponent + { + return simulation.Resolve().HasComponent(entity); + } + + public static T Comp(this ISimulation simulation, EntityUid entity) where T : IComponent + { + return simulation.Resolve().GetComponent(entity); + } + + public static TransformComponent Transform(this ISimulation simulation, EntityUid entity) + { + return simulation.Comp(entity); + } + } + public delegate void DiContainerDelegate(IDependencyCollection diContainer); public delegate void CompRegistrationDelegate(IComponentFactory factory); diff --git a/Robust.UnitTesting/Shared/EntityLookup_Test.cs b/Robust.UnitTesting/Shared/EntityLookup_Test.cs index b7ce0778250..24bae21a661 100644 --- a/Robust.UnitTesting/Shared/EntityLookup_Test.cs +++ b/Robust.UnitTesting/Shared/EntityLookup_Test.cs @@ -18,6 +18,12 @@ public sealed class EntityLookupTest { private static readonly MapId MapId = new MapId(1); + private static readonly TestCaseData[] IntersectingCases = new[] + { + // Big offset + new TestCaseData(true, new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), 0.25f, true), + }; + private static readonly TestCaseData[] InRangeCases = new[] { new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), new MapCoordinates(Vector2.Zero, MapId), 0.5f, false), @@ -207,6 +213,32 @@ public void TestMapInRange(bool physics, MapCoordinates spawnPos, MapCoordinates mapManager.DeleteMap(spawnPos.MapId); } + [Test, TestCaseSource(nameof(IntersectingCases))] + public void TestGridIntersecting(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result) + { + var sim = RobustServerSimulation.NewSimulation(); + var server = sim.InitializeInstance(); + + var lookup = server.Resolve().GetEntitySystem(); + var entManager = server.Resolve(); + var mapManager = server.Resolve(); + var mapSystem = entManager.System(); + + mapSystem.CreateMap(spawnPos.MapId); + var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager); + + if (physics) + GetPhysicsEntity(entManager, spawnPos); + else + entManager.Spawn(null, spawnPos); + + _ = entManager.SpawnEntity(null, spawnPos); + var bounds = new Box2Rotated(Box2.CenteredAround(queryPos.Position, new Vector2(range, range))); + + Assert.That(lookup.GetEntitiesIntersecting(queryPos.MapId, bounds).Count > 0, Is.EqualTo(result)); + mapManager.DeleteMap(spawnPos.MapId); + } + [Test, TestCaseSource(nameof(InRangeCases))] public void TestGridInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result) { diff --git a/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs index dee615eb820..eaa4292db6a 100644 --- a/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/ComponentFactory_Tests.cs @@ -3,8 +3,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.GameObjects { [TestFixture] diff --git a/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs b/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs index 51c353d16a9..2960529f50f 100644 --- a/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs @@ -12,8 +12,6 @@ using Robust.Shared.Network; using Robust.Shared.Timing; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.GameObjects { public sealed class ContainerTests : RobustIntegrationTest diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityManager_Components_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/EntityManager_Components_Tests.cs index 2fa43f4c700..a535b1779a2 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityManager_Components_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityManager_Components_Tests.cs @@ -81,10 +81,7 @@ public void AddComponentTest() var (sim, coords) = SimulationFactory(); var entMan = sim.Resolve(); var entity = entMan.SpawnEntity(null, coords); - var component = new DummyComponent() - { - Owner = entity - }; + var component = new DummyComponent(); // Act entMan.AddComponent(entity, component); @@ -101,10 +98,7 @@ public void AddComponentOverwriteTest() var (sim, coords) = SimulationFactory(); var entMan = sim.Resolve(); var entity = entMan.SpawnEntity(null, coords); - var component = new DummyComponent() - { - Owner = entity - }; + var component = new DummyComponent(); // Act entMan.AddComponent(entity, component, true); @@ -121,10 +115,10 @@ public void AddComponent_ExistingDeleted() var (sim, coords) = SimulationFactory(); var entMan = sim.Resolve(); var entity = entMan.SpawnEntity(null, coords); - var firstComp = new DummyComponent {Owner = entity}; + var firstComp = new DummyComponent(); entMan.AddComponent(entity, firstComp); entMan.RemoveComponent(entity); - var secondComp = new DummyComponent { Owner = entity }; + var secondComp = new DummyComponent(); // Act entMan.AddComponent(entity, secondComp); diff --git a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs index e260e50da78..236c9469357 100644 --- a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs @@ -13,15 +13,12 @@ using Robust.Shared.Reflection; using Robust.UnitTesting.Server; -// ReSharper disable AccessToStaticMemberViaDerivedType namespace Robust.UnitTesting.Shared.GameObjects.Systems { [TestFixture, Parallelizable] public sealed partial class AnchoredSystemTests { - private sealed class Subscriber : IEntityEventSubscriber { } - private const string Prototypes = @" - type: entity name: anchoredEnt @@ -30,7 +27,7 @@ private sealed class Subscriber : IEntityEventSubscriber { } - type: Transform anchored: true"; - private static (ISimulation, EntityUid gridId, MapCoordinates) SimulationFactory() + private static (ISimulation, Entity grid, MapCoordinates, SharedTransformSystem xformSys, SharedMapSystem mapSys) SimulationFactory() { var sim = RobustServerSimulation .NewSimulation() @@ -49,7 +46,7 @@ private static (ISimulation, EntityUid gridId, MapCoordinates) SimulationFactory // Add grid 1, as the default grid to anchor things to. var grid = mapManager.CreateGridEntity(testMapId); - return (sim, grid, coords); + return (sim, grid, coords, sim.System(), sim.System()); } // An entity is anchored to the tile it is over on the target grid. @@ -68,25 +65,23 @@ private static (ISimulation, EntityUid gridId, MapCoordinates) SimulationFactory [Test] public void OnAnchored_WorldPosition_TileCenter() { - var (sim, gridId, coordinates) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); - var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after + var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after // Act - entMan.System().ResetCounters(); - entMan.GetComponent(ent1).Anchored = true; - Assert.That(entMan.GetComponent(ent1).WorldPosition, Is.EqualTo(new Vector2(7.5f, 7.5f))); // centered on tile - entMan.System().AssertMoved(false); + sim.System().ResetCounters(); + xformSys.AnchorEntity(ent1); + Assert.That(xformSys.GetWorldPosition(ent1), Is.EqualTo(new Vector2(7.5f, 7.5f))); // centered on tile + sim.System().AssertMoved(false); } [ComponentProtoName("AnchorOnInit")] [Reflect(false)] - private sealed partial class AnchorOnInitComponent : Component { }; + private sealed partial class AnchorOnInitComponent : Component; [Reflect(false)] private sealed class AnchorOnInitTestSystem : EntitySystem @@ -117,9 +112,9 @@ public override void Shutdown() _transform.OnGlobalMoveEvent -= OnMove; } - public bool FailOnMove = false; - public int MoveCounter = 0; - public int ParentCounter = 0; + public bool FailOnMove; + public int MoveCounter; + public int ParentCounter; private void OnMove(ref MoveEvent ev) { @@ -163,25 +158,27 @@ public void OnInitAnchored_AddedToLookup() .RegisterComponents(f => f.RegisterClass()) .InitializeInstance(); + var mapSys = sim.System(); + var entMan = sim.Resolve(); var mapMan = sim.Resolve(); var mapId = sim.CreateMap().MapId; - var grid = mapMan.CreateGrid(mapId); + var grid = mapMan.CreateGridEntity(mapId); var coordinates = new MapCoordinates(new Vector2(7, 7), mapId); - var pos = grid.TileIndicesFor(coordinates); - grid.SetTile(pos, new Tile(1)); + var pos = mapSys.TileIndicesFor(grid, coordinates); + mapSys.SetTile(grid, pos, new Tile(1)); var ent1 = entMan.SpawnEntity(null, coordinates); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); - Assert.That(!grid.GetAnchoredEntities(pos).Any()); + Assert.That(sim.Transform(ent1).Anchored, Is.False); + Assert.That(!mapSys.GetAnchoredEntities(grid, pos).Any()); entMan.DeleteEntity(ent1); var ent2 = entMan.CreateEntityUninitialized(null, coordinates); entMan.AddComponent(ent2); entMan.InitializeAndStartEntity(ent2); - Assert.That(entMan.GetComponent(ent2).Anchored); - Assert.That(grid.GetAnchoredEntities(pos).Count(), Is.EqualTo(1)); - Assert.That(grid.GetAnchoredEntities(pos).Contains(ent2)); + Assert.That(sim.Transform(ent2).Anchored); + Assert.That(mapSys.GetAnchoredEntities(grid, pos).Count(), Is.EqualTo(1)); + Assert.That(mapSys.GetAnchoredEntities(grid, pos).Contains(ent2)); } /// @@ -190,22 +187,20 @@ public void OnInitAnchored_AddedToLookup() [Test] public void OnAnchored_Parent_SetToGrid() { - var (sim, gridId, coordinates) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); - var traversal = entMan.System(); + var traversal = sim.System(); traversal.Enabled = false; - var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after + var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after // Act - entMan.System().ResetCounters(); - entMan.GetComponent(ent1).Anchored = true; - Assert.That(entMan.GetComponent(ent1).ParentUid, Is.EqualTo(grid.Owner)); - entMan.System().AssertMoved(); + sim.System().ResetCounters(); + sim.Transform(ent1).Anchored = true; + Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner)); + sim.System().AssertMoved(); traversal.Enabled = true; } @@ -215,19 +210,17 @@ public void OnAnchored_Parent_SetToGrid() [Test] public void OnAnchored_EmptyTile_Nop() { - var (sim, gridId, coords) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, Tile.Empty); + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, Tile.Empty); // Act - entMan.GetComponent(ent1).Anchored = true; + xformSys.AnchorEntity(ent1); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(Tile.Empty)); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty)); } /// @@ -237,22 +230,20 @@ public void OnAnchored_EmptyTile_Nop() [Test] public void OnAnchored_NonEmptyTile_Anchors() { - var (sim, gridId, coords) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); // Act - entMan.GetComponent(ent1).Anchored = true; + sim.Transform(ent1).Anchored = true; - Assert.That(grid.GetAnchoredEntities(tileIndices).First(), Is.EqualTo(ent1)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.Not.EqualTo(Tile.Empty)); - Assert.That(entMan.HasComponent(ent1), Is.False); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty)); + Assert.That(sim.HasComp(ent1), Is.False); var tempQualifier = grid.Owner; - Assert.That(entMan.HasComponent(tempQualifier), Is.True); + Assert.That(sim.HasComp(tempQualifier), Is.True); } /// @@ -263,26 +254,24 @@ public void OnAnchored_NonEmptyTile_Anchors() [Test] public void Anchored_SetPosition_Nop() { - var (sim, gridId, coordinates) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); // coordinates are already tile centered to prevent snapping and MoveEvent coordinates = coordinates.Offset(new Vector2(0.5f, 0.5f)); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); - var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after - entMan.GetComponent(ent1).Anchored = true; // Anchoring will change parent if needed, raising MoveEvent, subscribe after - entMan.System().FailOnMove = true; + var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after + sim.Transform(ent1).Anchored = true; + sim.System().FailOnMove = true; // Act - entMan.GetComponent(ent1).WorldPosition = new Vector2(99, 99); - entMan.GetComponent(ent1).LocalPosition = new Vector2(99, 99); + sim.Transform(ent1).WorldPosition = new Vector2(99, 99); + sim.Transform(ent1).LocalPosition = new Vector2(99, 99); - Assert.That(entMan.GetComponent(ent1).MapPosition, Is.EqualTo(coordinates)); - entMan.System().FailOnMove = false; + Assert.That(xformSys.GetMapCoordinates(ent1), Is.EqualTo(coordinates)); + sim.System().FailOnMove = false; } /// @@ -291,23 +280,19 @@ public void Anchored_SetPosition_Nop() [Test] public void Anchored_ChangeParent_Unanchors() { - var (sim, gridId, coordinates) = SimulationFactory(); - var entMan = sim.Resolve(); - var mapMan = sim.Resolve(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); - var grid = entMan.GetComponent(gridId); - - var ent1 = entMan.SpawnEntity(null, coordinates); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); - entMan.GetComponent(ent1).Anchored = true; + var ent1 = sim.SpawnEntity(null, coordinates); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); + xformSys.AnchorEntity(ent1); // Act - entMan.EntitySysManager.GetEntitySystem().SetParent(ent1, mapMan.GetMapEntityId(coordinates.MapId)); + xformSys.SetParent(ent1, mapSys.GetMap(coordinates.MapId)); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(new Tile(1))); + Assert.That(sim.Transform(ent1).Anchored, Is.False); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1))); } /// @@ -318,20 +303,19 @@ public void Anchored_ChangeParent_Unanchors() [Test] public void Anchored_SetParentSame_Nop() { - var (sim, gridId, coords) = SimulationFactory(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var grid = entMan.GetComponent(gridId); var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); - entMan.GetComponent(ent1).Anchored = true; + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); + sim.Transform(ent1).Anchored = true; // Act - entMan.EntitySysManager.GetEntitySystem().SetParent(ent1, grid.Owner); + xformSys.SetParent(ent1, grid.Owner); - Assert.That(grid.GetAnchoredEntities(tileIndices).First(), Is.EqualTo(ent1)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.Not.EqualTo(Tile.Empty)); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty)); } /// @@ -340,22 +324,20 @@ public void Anchored_SetParentSame_Nop() [Test] public void Anchored_TileToSpace_Unanchors() { - var (sim, gridId, coords) = SimulationFactory(); - var entMan = sim.Resolve(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); - grid.SetTile(new Vector2i(100, 100), new Tile(1)); // Prevents the grid from being deleted when the Act happens - entMan.GetComponent(ent1).Anchored = true; + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); + mapSys.SetTile(grid, new Vector2i(100, 100), new Tile(1)); // Prevents the grid from being deleted when the Act happens + xformSys.AnchorEntity(ent1); // Act - grid.SetTile(tileIndices, Tile.Empty); + mapSys.SetTile(grid, tileIndices, Tile.Empty); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(Tile.Empty)); + Assert.That(sim.Transform(ent1).Anchored, Is.False); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty)); } /// @@ -368,25 +350,24 @@ public void Anchored_TileToSpace_Unanchors() [Test] public void Anchored_AddToContainer_Unanchors() { - var (sim, gridId, coords) = SimulationFactory(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); - entMan.GetComponent(ent1).Anchored = true; + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); + xformSys.AnchorEntity(ent1); // Act // We purposefully use the grid as container so parent stays the same, reparent will unanchor var containerSys = entMan.System(); - var containerMan = entMan.AddComponent(gridId); - var container = containerSys.MakeContainer(gridId, "TestContainer", containerMan); + var containerMan = entMan.AddComponent(grid); + var container = containerSys.MakeContainer(grid, "TestContainer", containerMan); containerSys.Insert(ent1, container); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(new Tile(1))); + Assert.That(sim.Transform(ent1).Anchored, Is.False); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1))); Assert.That(container.ContainedEntities.Count, Is.EqualTo(1)); } @@ -396,14 +377,13 @@ public void Anchored_AddToContainer_Unanchors() [Test] public void Anchored_AddPhysComp_IsStaticBody() { - var (sim, gridId, coords) = SimulationFactory(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); - entMan.GetComponent(ent1).Anchored = true; + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); + xformSys.AnchorEntity(ent1); // Act // assumed default body is Dynamic @@ -418,20 +398,19 @@ public void Anchored_AddPhysComp_IsStaticBody() [Test] public void OnAnchored_HasPhysicsComp_IsStaticBody() { - var (sim, gridId, coordinates) = SimulationFactory(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var physSystem = sim.Resolve().GetEntitySystem(); + var physSystem = sim.System(); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); var ent1 = entMan.SpawnEntity(null, coordinates); var physComp = entMan.AddComponent(ent1); physSystem.SetBodyType(ent1, BodyType.Dynamic, body: physComp); // Act - entMan.GetComponent(ent1).Anchored = true; + xformSys.AnchorEntity(ent1); Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Static)); } @@ -442,18 +421,17 @@ public void OnAnchored_HasPhysicsComp_IsStaticBody() [Test] public void OnUnanchored_HasPhysicsComp_IsDynamicBody() { - var (sim, gridId, coords) = SimulationFactory(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); var physComp = entMan.AddComponent(ent1); - entMan.GetComponent(ent1).Anchored = true; + sim.Transform(ent1).Anchored = true; // Act - entMan.GetComponent(ent1).Anchored = false; + xformSys.Unanchor(ent1); Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Dynamic)); } @@ -464,18 +442,15 @@ public void OnUnanchored_HasPhysicsComp_IsDynamicBody() [Test] public void SpawnAnchored_EmptyTile_Unanchors() { - var (sim, gridId, coords) = SimulationFactory(); - var entMan = sim.Resolve(); - - var grid = entMan.GetComponent(gridId); + var (sim, grid, coords, _, mapSys) = SimulationFactory(); // Act - var ent1 = entMan.SpawnEntity("anchoredEnt", coords); + var ent1 = sim.SpawnEntity("anchoredEnt", coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(Tile.Empty)); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty)); + Assert.That(sim.Transform(ent1).Anchored, Is.False); } /// @@ -484,25 +459,24 @@ public void SpawnAnchored_EmptyTile_Unanchors() [Test] public void OnAnchored_InContainer_Nop() { - var (sim, gridId, coords) = SimulationFactory(); + var (sim, grid, coords, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); - var grid = entMan.GetComponent(gridId); - var ent1 = entMan.SpawnEntity(null, coords); - var tileIndices = grid.TileIndicesFor(entMan.GetComponent(ent1).Coordinates); - grid.SetTile(tileIndices, new Tile(1)); + var ent1 = sim.SpawnEntity(null, coords); + var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates); + mapSys.SetTile(grid, tileIndices, new Tile(1)); var containerSys = entMan.System(); - var containerMan = entMan.AddComponent(gridId); - var container = containerSys.MakeContainer(gridId, "TestContainer", containerMan); + var containerMan = entMan.AddComponent(grid); + var container = containerSys.MakeContainer(grid, "TestContainer", containerMan); containerSys.Insert(ent1, container); // Act - entMan.GetComponent(ent1).Anchored = true; + xformSys.AnchorEntity(ent1); - Assert.That(entMan.GetComponent(ent1).Anchored, Is.False); - Assert.That(grid.GetAnchoredEntities(tileIndices).Count(), Is.EqualTo(0)); - Assert.That(grid.GetTileRef(tileIndices).Tile, Is.EqualTo(new Tile(1))); + Assert.That(sim.Transform(ent1).Anchored, Is.False); + Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0)); + Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1))); Assert.That(container.ContainedEntities.Count, Is.EqualTo(1)); } @@ -512,23 +486,20 @@ public void OnAnchored_InContainer_Nop() [Test] public void Unanchored_Unanchor_Nop() { - var (sim, gridId, coordinates) = SimulationFactory(); - var entMan = sim.Resolve(); - var mapMan = sim.Resolve(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); - var traversal = entMan.System(); + var traversal = sim.System(); traversal.Enabled = false; - var ent1 = entMan.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after + var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after // Act - entMan.System().FailOnMove = true; - entMan.GetComponent(ent1).Anchored = false; - Assert.That(entMan.GetComponent(ent1).ParentUid, Is.EqualTo(mapMan.GetMapEntityId(coordinates.MapId))); - entMan.System().FailOnMove = false; + sim.System().FailOnMove = true; + xformSys.Unanchor(ent1); + Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(mapSys.GetMap(coordinates.MapId))); + sim.System().FailOnMove = false; traversal.Enabled = true; } @@ -538,17 +509,16 @@ public void Unanchored_Unanchor_Nop() [Test] public void Anchored_Unanchored_ParentUnchanged() { - var (sim, gridId, coordinates) = SimulationFactory(); + var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory(); var entMan = sim.Resolve(); // can only be anchored to a tile - var grid = entMan.GetComponent(gridId); - grid.SetTile(grid.TileIndicesFor(coordinates), new Tile(1)); - var ent1 = entMan.SpawnEntity("anchoredEnt", grid.MapToGrid(coordinates)); + mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1)); + var ent1 = entMan.SpawnEntity("anchoredEnt", mapSys.MapToGrid(grid, coordinates)); - entMan.GetComponent(ent1).Anchored = false; + xformSys.Unanchor(ent1); - Assert.That(entMan.GetComponent(ent1).ParentUid, Is.EqualTo(grid.Owner)); + Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner)); } } } diff --git a/Robust.UnitTesting/Shared/GameObjects/TransformComponent_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/TransformComponent_Tests.cs index 336c9e02e78..7e151e089c0 100644 --- a/Robust.UnitTesting/Shared/GameObjects/TransformComponent_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/TransformComponent_Tests.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using NUnit.Framework; +using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -21,6 +22,7 @@ public void TestGetWorldMatches() var entManager = server.Resolve(); entManager.System().CreateMap(out var mapId); + var xform = entManager.System(); var ent1 = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId)); var ent2 = entManager.SpawnEntity(null, new MapCoordinates(new Vector2(100f, 0f), mapId)); @@ -28,19 +30,19 @@ public void TestGetWorldMatches() var xform1 = entManager.GetComponent(ent1); var xform2 = entManager.GetComponent(ent2); - xform2.AttachParent(xform1); + xform.SetParent(ent2, ent1); xform1.LocalRotation = MathF.PI; - var (worldPos, worldRot, worldMatrix) = xform2.GetWorldPositionRotationMatrix(); + var (worldPos, worldRot, worldMatrix) = xform.GetWorldPositionRotationMatrix(xform2); - Assert.That(worldPos, Is.EqualTo(xform2.WorldPosition)); - Assert.That(worldRot, Is.EqualTo(xform2.WorldRotation)); - Assert.That(worldMatrix, Is.EqualTo(xform2.WorldMatrix)); + Assert.That(worldPos, Is.EqualTo(xform.GetWorldPosition(xform2))); + Assert.That(worldRot, Is.EqualTo(xform.GetWorldRotation(xform2))); + Assert.That(worldMatrix, Is.EqualTo(xform.GetWorldMatrix(xform2))); - var (_, _, invWorldMatrix) = xform2.GetWorldPositionRotationInvMatrix(); + var (_, _, invWorldMatrix) = xform.GetWorldPositionRotationInvMatrix(xform2); - Assert.That(invWorldMatrix, Is.EqualTo(xform2.InvWorldMatrix)); + Assert.That(invWorldMatrix, Is.EqualTo(xform.GetInvWorldMatrix(xform2))); } /// @@ -53,20 +55,21 @@ public void AttachToGridOrMap() var entManager = server.Resolve(); var mapManager = server.Resolve(); + var mapSystem = entManager.System(); + var xformSystem = entManager.System(); - entManager.System().CreateMap(out var mapId); + mapSystem.CreateMap(out var mapId); var grid = mapManager.CreateGridEntity(mapId); - grid.Comp.SetTile(new Vector2i(0, 0), new Tile(1)); - var gridXform = entManager.GetComponent(grid); - gridXform.LocalPosition = new Vector2(0f, 100f); + mapSystem.SetTile(grid, new Vector2i(0, 0), new Tile(1)); + xformSystem.SetLocalPosition(grid, new Vector2(0f, 100f)); var ent1 = entManager.SpawnEntity(null, new EntityCoordinates(grid, Vector2.One * grid.Comp.TileSize / 2)); var ent2 = entManager.SpawnEntity(null, new EntityCoordinates(ent1, Vector2.Zero)); var xform2 = entManager.GetComponent(ent2); - Assert.That(xform2.WorldPosition, Is.EqualTo(new Vector2(0.5f, 100.5f))); + Assert.That(xformSystem.GetWorldPosition(ent2), Is.EqualTo(new Vector2(0.5f, 100.5f))); - xform2.AttachToGridOrMap(); + xformSystem.AttachToGridOrMap(ent2); Assert.That(xform2.LocalPosition, Is.EqualTo(Vector2.One * grid.Comp.TileSize / 2)); } } diff --git a/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs b/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs index 2d0d303b29b..0a3f51c352d 100644 --- a/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs +++ b/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs @@ -38,7 +38,11 @@ public async Task UnknownEntityTest() server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, true)); // Set up map. - var map = server.System().CreateMap(); + EntityUid map = default; + server.Post(() => + { + map = server.System().CreateMap(); + }); await RunTicks(); @@ -157,7 +161,11 @@ public async Task UnknownEntityDeleteTest() server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, true)); // Set up map. - var map = server.System().CreateMap(); + EntityUid map = default; + server.Post(() => + { + map = server.System().CreateMap(); + }); await RunTicks(); diff --git a/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs b/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs index 4185e9524b7..85021a7b196 100644 --- a/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs +++ b/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs @@ -10,8 +10,6 @@ using Robust.Shared.Network; using Robust.Shared.Player; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.GameState; /// diff --git a/Robust.UnitTesting/Shared/GameState/NoSharedReferencesTest.cs b/Robust.UnitTesting/Shared/GameState/NoSharedReferencesTest.cs index bc036f1328e..b24218bd6f9 100644 --- a/Robust.UnitTesting/Shared/GameState/NoSharedReferencesTest.cs +++ b/Robust.UnitTesting/Shared/GameState/NoSharedReferencesTest.cs @@ -37,7 +37,10 @@ public async Task ReferencesAreNotShared() client.Post(() => netMan.ClientConnect(null!, 0, null!)); // Set up map. - var map = server.System().CreateMap(); + server.Post(() => + { + server.System().CreateMap(); + }); await RunTicks(); diff --git a/Robust.UnitTesting/Shared/Map/EntityCoordinates_Tests.cs b/Robust.UnitTesting/Shared/Map/EntityCoordinates_Tests.cs index 2ce5d6418e4..f7f3c7bf3d4 100644 --- a/Robust.UnitTesting/Shared/Map/EntityCoordinates_Tests.cs +++ b/Robust.UnitTesting/Shared/Map/EntityCoordinates_Tests.cs @@ -7,7 +7,6 @@ using Robust.Shared.Serialization.Manager; // ReSharper disable InconsistentNaming -// ReSharper disable AccessToStaticMemberViaDerivedType namespace Robust.UnitTesting.Shared.Map { [TestFixture, Parallelizable, TestOf(typeof(EntityCoordinates))] diff --git a/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs b/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs index cb27151cbde..7702d319194 100644 --- a/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs +++ b/Robust.UnitTesting/Shared/Map/GridFixtures_Tests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Threading.Tasks; @@ -8,62 +9,92 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; +using Robust.UnitTesting.Server; -namespace Robust.UnitTesting.Shared.Map +namespace Robust.UnitTesting.Shared.Map; + +/// +/// Tests whether grid fixtures are being generated correctly. +/// +[Parallelizable(ParallelScope.All)] +[TestFixture] +public sealed class GridFixtures_Tests : RobustIntegrationTest { /// - /// Tests whether grid fixtures are being generated correctly. + /// Tests that grid fixtures match what's expected. /// - [Parallelizable(ParallelScope.All)] - [TestFixture] - public sealed class GridFixtures_Tests : RobustIntegrationTest + [Test] + public void TestGridFixtureDeletion() { - [Test] - public async Task TestGridFixtures() + var server = RobustServerSimulation.NewSimulation().InitializeInstance(); + var map = server.CreateMap(); + var entManager = server.Resolve(); + var grid = server.Resolve().CreateGridEntity(map.MapId); + var mapSystem = entManager.System(); + var fixtures = entManager.GetComponent(grid); + + mapSystem.SetTiles(grid, new List<(Vector2i GridIndices, Tile Tile)>() { - var server = StartServer(); - await server.WaitIdleAsync(); - - var entManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); - var physSystem = server.ResolveDependency().GetEntitySystem(); - - await server.WaitAssertion(() => - { - entManager.System().CreateMap(out var mapId); - var grid = mapManager.CreateGridEntity(mapId); - - // Should be nothing if grid empty - Assert.That(entManager.TryGetComponent(grid, out PhysicsComponent? gridBody)); - Assert.That(entManager.TryGetComponent(grid, out FixturesComponent? manager)); - Assert.That(manager!.FixtureCount, Is.EqualTo(0)); - Assert.That(gridBody!.BodyType, Is.EqualTo(BodyType.Static)); - - // 1 fixture if we only ever update the 1 chunk - grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); - - Assert.That(manager.FixtureCount, Is.EqualTo(1)); - // Also should only be a single tile. - var bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); - // Poly probably has Box2D's radius added to it so won't be a unit square - Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 1.0f, 0.1f)); - - // Now do 2 tiles (same chunk) - grid.Comp.SetTile(new Vector2i(0, 1), new Tile(1)); - - Assert.That(manager.FixtureCount, Is.EqualTo(1)); - bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); - - // Even if we add a new tile old fixture should stay the same if they don't connect. - Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 2.0f, 0.1f)); - - // If we add a new chunk should be 2 now - grid.Comp.SetTile(new Vector2i(0, -1), new Tile(1)); - Assert.That(manager.FixtureCount, Is.EqualTo(2)); - - physSystem.SetLinearVelocity(grid, Vector2.One, manager: manager, body: gridBody); - Assert.That(gridBody.LinearVelocity.Length, Is.EqualTo(0f)); - }); - } + (Vector2i.Zero, new Tile(1)), + (Vector2i.Right, new Tile(1)), + (Vector2i.Right * 2, new Tile(1)), + (Vector2i.Up, new Tile(1)), + }); + + Assert.That(fixtures.FixtureCount, Is.EqualTo(2)); + Assert.That(grid.Comp.LocalAABB.Equals(new Box2(0f, 0f, 3f, 2f))); + + mapSystem.SetTile(grid, Vector2i.Up, Tile.Empty); + + Assert.That(fixtures.FixtureCount, Is.EqualTo(1)); + Assert.That(grid.Comp.LocalAABB.Equals(new Box2(0f, 0f, 3f, 1f))); + } + + [Test] + public async Task TestGridFixtures() + { + var server = StartServer(); + await server.WaitIdleAsync(); + + var entManager = server.ResolveDependency(); + var mapManager = server.ResolveDependency(); + var physSystem = server.ResolveDependency().GetEntitySystem(); + + await server.WaitAssertion(() => + { + entManager.System().CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); + + // Should be nothing if grid empty + Assert.That(entManager.TryGetComponent(grid, out PhysicsComponent? gridBody)); + Assert.That(entManager.TryGetComponent(grid, out FixturesComponent? manager)); + Assert.That(manager!.FixtureCount, Is.EqualTo(0)); + Assert.That(gridBody!.BodyType, Is.EqualTo(BodyType.Static)); + + // 1 fixture if we only ever update the 1 chunk + grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); + + Assert.That(manager.FixtureCount, Is.EqualTo(1)); + // Also should only be a single tile. + var bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); + // Poly probably has Box2D's radius added to it so won't be a unit square + Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 1.0f, 0.1f)); + + // Now do 2 tiles (same chunk) + grid.Comp.SetTile(new Vector2i(0, 1), new Tile(1)); + + Assert.That(manager.FixtureCount, Is.EqualTo(1)); + bounds = manager.Fixtures.First().Value.Shape.ComputeAABB(new Transform(Vector2.Zero, (float) Angle.Zero.Theta), 0); + + // Even if we add a new tile old fixture should stay the same if they don't connect. + Assert.That(MathHelper.CloseToPercent(Box2.Area(bounds), 2.0f, 0.1f)); + + // If we add a new chunk should be 2 now + grid.Comp.SetTile(new Vector2i(0, -1), new Tile(1)); + Assert.That(manager.FixtureCount, Is.EqualTo(2)); + + physSystem.SetLinearVelocity(grid, Vector2.One, manager: manager, body: gridBody); + Assert.That(gridBody.LinearVelocity.Length, Is.EqualTo(0f)); + }); } } diff --git a/Robust.UnitTesting/Shared/Map/MapPauseTests.cs b/Robust.UnitTesting/Shared/Map/MapPauseTests.cs index 0f4fdbc1467..6d39b5f9f34 100644 --- a/Robust.UnitTesting/Shared/Map/MapPauseTests.cs +++ b/Robust.UnitTesting/Shared/Map/MapPauseTests.cs @@ -4,8 +4,6 @@ using Robust.Shared.Map; using Robust.UnitTesting.Server; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Map; [TestFixture] diff --git a/Robust.UnitTesting/Shared/Maths/NumericsHelpers_Test.cs b/Robust.UnitTesting/Shared/Maths/NumericsHelpers_Test.cs index 7729eb3d1fc..575338755d7 100644 --- a/Robust.UnitTesting/Shared/Maths/NumericsHelpers_Test.cs +++ b/Robust.UnitTesting/Shared/Maths/NumericsHelpers_Test.cs @@ -12,7 +12,6 @@ namespace Robust.UnitTesting.Shared.Maths [Parallelizable(ParallelScope.All | ParallelScope.Fixtures)] [TestFixture] [TestOf(typeof(NumericsHelpers))] - [SuppressMessage("ReSharper", "AccessToStaticMemberViaDerivedType")] public sealed class NumericsHelpers_Test { #region Utils diff --git a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs index b8d83181008..6b8de6fe526 100644 --- a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs +++ b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs @@ -15,8 +15,6 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Physics; public sealed class BroadphaseNetworkingTest : RobustIntegrationTest diff --git a/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs b/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs index 8aac3e90649..85b1be0f659 100644 --- a/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Broadphase_Test.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; using NUnit.Framework; @@ -25,11 +26,13 @@ public void ReparentSundries() var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); var entManager = sim.Resolve(); var mapManager = sim.Resolve(); + var mapSys = entManager.System(); + var xformSys = entManager.System(); var (mapEnt, mapId) = sim.CreateMap(); var grid = mapManager.CreateGridEntity(mapId); - grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); + mapSys.SetTile(grid, Vector2i.Zero, new Tile(1)); Assert.That(entManager.HasComponent(grid)); var broadphase = entManager.GetComponent(grid); @@ -40,7 +43,7 @@ public void ReparentSundries() var broadphaseData = xform.Broadphase; Assert.That(broadphaseData!.Value.Uid, Is.EqualTo(grid.Owner)); - xform.Coordinates = new EntityCoordinates(mapEnt, Vector2.One); + xformSys.SetCoordinates(ent, new EntityCoordinates(mapEnt, Vector2.One)); Assert.That(broadphase.SundriesTree, Does.Not.Contain(ent)); Assert.That(entManager.GetComponent(mapEnt).SundriesTree, Does.Contain(ent)); @@ -59,12 +62,14 @@ public void ReparentBroadphase() var mapManager = sim.Resolve(); var fixturesSystem = entManager.EntitySysManager.GetEntitySystem(); var physicsSystem = entManager.EntitySysManager.GetEntitySystem(); + var mapSys = entManager.System(); + var xformSys = entManager.System(); var (mapEnt, mapId) = sim.CreateMap(); var grid = mapManager.CreateGridEntity(mapId); var gridUid = grid.Owner; - grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); + mapSys.SetTile(grid, Vector2i.Zero, new Tile(1)); Assert.That(entManager.HasComponent(gridUid)); var broadphase = entManager.GetComponent(gridUid); @@ -90,7 +95,7 @@ public void ReparentBroadphase() Assert.That(broadphase.StaticTree.GetProxy(fixture.Proxies[0].ProxyId)!.Equals(fixture.Proxies[0])); // Now check we go to the map's tree correctly. - xform.Coordinates = new EntityCoordinates(mapEnt, Vector2.One); + xformSys.SetCoordinates(ent, new EntityCoordinates(mapEnt, Vector2.One)); Assert.That(entManager.GetComponent(mapEnt).StaticTree.GetProxy(fixture.Proxies[0].ProxyId)!.Equals(fixture.Proxies[0])); Assert.That(xform.Broadphase!.Value.Uid.Equals(mapEnt)); } @@ -107,21 +112,22 @@ public void GridMapUpdate() var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); var entManager = sim.Resolve(); var mapManager = sim.Resolve(); + var mapSys = entManager.System(); + var xformSys = entManager.System(); - var mapId1 = sim.CreateMap().MapId; - var mapId2 = sim.CreateMap().MapId; + var (map1, mapId1) = sim.CreateMap(); + var (map2, _) = sim.CreateMap(); var grid = mapManager.CreateGridEntity(mapId1); - var xform = entManager.GetComponent(grid); - grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); - var mapBroadphase1 = entManager.GetComponent(mapManager.GetMapEntityId(mapId1)); - var mapBroadphase2 = entManager.GetComponent(mapManager.GetMapEntityId(mapId2)); + mapSys.SetTile(grid, Vector2i.Zero, new Tile(1)); + var mapBroadphase1 = entManager.GetComponent(map1); + var mapBroadphase2 = entManager.GetComponent(map2); entManager.TickUpdate(0.016f, false); #pragma warning disable NUnit2046 Assert.That(mapBroadphase1.DynamicTree.Count, Is.EqualTo(0)); #pragma warning restore NUnit2046 - xform.Coordinates = new EntityCoordinates(mapManager.GetMapEntityId(mapId2), Vector2.Zero); + xformSys.SetCoordinates(grid, new EntityCoordinates(map1, Vector2.One)); entManager.TickUpdate(0.016f, false); #pragma warning disable NUnit2046 Assert.That(mapBroadphase2.DynamicTree.Count, Is.EqualTo(0)); @@ -140,13 +146,14 @@ public void BroadphaseRecursiveUpdate() var system = entManager.EntitySysManager; var physicsSystem = system.GetEntitySystem(); var lookup = system.GetEntitySystem(); + var mapSys = entManager.System(); - var mapId = sim.CreateMap().MapId; + var (map, mapId) = sim.CreateMap(); var grid = mapManager.CreateGridEntity(mapId); - grid.Comp.SetTile(Vector2i.Zero, new Tile(1)); + mapSys.SetTile(grid, Vector2i.Zero, new Tile(1)); var gridBroadphase = entManager.GetComponent(grid); - var mapBroadphase = entManager.GetComponent(mapManager.GetMapEntityId(mapId)); + var mapBroadphase = entManager.GetComponent(map); Assert.That(entManager.EntityQuery(true).Count(), Is.EqualTo(2)); @@ -169,7 +176,7 @@ public void BroadphaseRecursiveUpdate() Assert.That(lookup.FindBroadphase(child1), Is.EqualTo(gridBroadphase)); // They should get deparented to the map and updated to the map's broadphase instead. - grid.Comp.SetTile(Vector2i.Zero, Tile.Empty); + mapSys.SetTile(grid, Vector2i.Zero, Tile.Empty); Assert.That(lookup.FindBroadphase(parent), Is.EqualTo(mapBroadphase)); Assert.That(lookup.FindBroadphase(child1), Is.EqualTo(mapBroadphase)); Assert.That(lookup.FindBroadphase(child2), Is.EqualTo(mapBroadphase)); @@ -191,6 +198,7 @@ public void EntMapChangeRecursiveUpdate() var xforms = system.GetEntitySystem(); var physSystem = system.GetEntitySystem(); var fixtures = system.GetEntitySystem(); + var mapSys = entManager.System(); // setup maps var (mapA, mapAId) = sim.CreateMap(); @@ -204,9 +212,9 @@ public void EntMapChangeRecursiveUpdate() var gridB = gridBComp.Owner; var gridC = gridCComp.Owner; xforms.SetLocalPosition(gridC, new Vector2(10, 10)); - gridAComp.Comp.SetTile(Vector2i.Zero, new Tile(1)); - gridBComp.Comp.SetTile(Vector2i.Zero, new Tile(1)); - gridCComp.Comp.SetTile(Vector2i.Zero, new Tile(1)); + mapSys.SetTile(gridAComp, Vector2i.Zero, new Tile(1)); + mapSys.SetTile(gridBComp, Vector2i.Zero, new Tile(1)); + mapSys.SetTile(gridCComp, Vector2i.Zero, new Tile(1)); // set up test entities var parent = entManager.SpawnEntity(null, new EntityCoordinates(mapA, new Vector2(200,200))); diff --git a/Robust.UnitTesting/Shared/Physics/Collision_Test.cs b/Robust.UnitTesting/Shared/Physics/Collision_Test.cs index 7a5dc3c08ed..ebee07a62bc 100644 --- a/Robust.UnitTesting/Shared/Physics/Collision_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Collision_Test.cs @@ -38,6 +38,30 @@ namespace Robust.UnitTesting.Shared.Physics; [TestFixture] public sealed class Collision_Test { + [Test] + public void TestHardCollidable() + { + var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + var entManager = sim.Resolve(); + + var fixtures = entManager.System(); + var physics = entManager.System(); + + var map = sim.CreateMap(); + + var bodyAUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero)); + var bodyBUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero)); + var bodyA = entManager.AddComponent(bodyAUid); + var bodyB = entManager.AddComponent(bodyBUid); + + Assert.That(!physics.IsHardCollidable(bodyAUid, bodyBUid)); + + fixtures.CreateFixture(bodyAUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); + fixtures.CreateFixture(bodyBUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true)); + + Assert.That(physics.IsHardCollidable(bodyAUid, bodyBUid)); + } + [Test] public void TestCollision() { diff --git a/Robust.UnitTesting/Shared/Physics/RecursiveUpdateTest.cs b/Robust.UnitTesting/Shared/Physics/RecursiveUpdateTest.cs index 8dcccbe3880..01dc9af570f 100644 --- a/Robust.UnitTesting/Shared/Physics/RecursiveUpdateTest.cs +++ b/Robust.UnitTesting/Shared/Physics/RecursiveUpdateTest.cs @@ -8,8 +8,6 @@ using Robust.Shared.Physics; using Robust.UnitTesting.Server; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Physics; [TestFixture] diff --git a/Robust.UnitTesting/Shared/Random/RandomExtensionsTests.cs b/Robust.UnitTesting/Shared/Random/RandomExtensionsTests.cs index 15c66fa0825..a93bb274c47 100644 --- a/Robust.UnitTesting/Shared/Random/RandomExtensionsTests.cs +++ b/Robust.UnitTesting/Shared/Random/RandomExtensionsTests.cs @@ -6,8 +6,6 @@ using Robust.Shared.Collections; using Robust.Shared.Random; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Random; /// Instantiable tests for . diff --git a/Robust.UnitTesting/Shared/Serialization/PropertyAndFieldDefinitionTest.cs b/Robust.UnitTesting/Shared/Serialization/PropertyAndFieldDefinitionTest.cs index f57724371c0..601251c8a4c 100644 --- a/Robust.UnitTesting/Shared/Serialization/PropertyAndFieldDefinitionTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/PropertyAndFieldDefinitionTest.cs @@ -9,7 +9,6 @@ // ReSharper disable UnassignedGetOnlyAutoProperty // ReSharper disable UnusedAutoPropertyAccessor.Global -// ReSharper disable AccessToStaticMemberViaDerivedType namespace Robust.UnitTesting.Shared.Serialization { diff --git a/Robust.UnitTesting/Shared/Serialization/SerializationPriorityTest.cs b/Robust.UnitTesting/Shared/Serialization/SerializationPriorityTest.cs index c9551a096c8..80817aeb6e4 100644 --- a/Robust.UnitTesting/Shared/Serialization/SerializationPriorityTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/SerializationPriorityTest.cs @@ -11,8 +11,6 @@ using Robust.Shared.Serialization.Markdown.Sequence; using YamlDotNet.RepresentationModel; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization { public sealed class SerializationPriorityTest : RobustUnitTest diff --git a/Robust.UnitTesting/Shared/Serialization/SerializationTests/DataDefinitionTests.cs b/Robust.UnitTesting/Shared/Serialization/SerializationTests/DataDefinitionTests.cs index 6030dfd108e..f2840126d91 100644 --- a/Robust.UnitTesting/Shared/Serialization/SerializationTests/DataDefinitionTests.cs +++ b/Robust.UnitTesting/Shared/Serialization/SerializationTests/DataDefinitionTests.cs @@ -23,7 +23,7 @@ public sealed partial class DataDefinitionTests : SerializationTest //copy: null <> cts(cc(+nt), c)/regular(c) [DataDefinition] - public partial class DataDefTestDummy + public sealed partial class DataDefTestDummy { [DataField("a")] public int a = Int32.MaxValue; [DataField("b")] public DataDummyStruct b = new(){Value = "default"}; diff --git a/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.Resources.cs b/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.Resources.cs index 15a0c5cd71b..a997865ad2d 100644 --- a/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.Resources.cs +++ b/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.Resources.cs @@ -185,7 +185,7 @@ public SerializerClass CreateCopy(ISerializationManager serializationManager, Se #region Other TypeDefinitions [CopyByRef] - private class CopyByRefTestClass {} + private sealed class CopyByRefTestClass {} [CopyByRef] private struct CopyByRefTestStruct diff --git a/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.cs b/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.cs index ad4dce76d37..6337b0dee68 100644 --- a/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.cs +++ b/Robust.UnitTesting/Shared/Serialization/SerializationTests/ManagerTests.cs @@ -137,8 +137,8 @@ todo test the generic variants too new object[] { SerializerRanDataNode, - SerializerClass.SerializerReturn, - SerializerClass.SerializerReturnAlt, + (object)SerializerClass.SerializerReturn, + (object)SerializerClass.SerializerReturnAlt, false, new Func[] { @@ -149,8 +149,8 @@ todo test the generic variants too new object[] { SerializerRanCustomDataNode, - SerializerClass.SerializerCustomReturn, - SerializerClass.SerializerCustomReturnAlt, + (object)SerializerClass.SerializerCustomReturn, + (object)SerializerClass.SerializerCustomReturnAlt, true, new Func[] { @@ -229,8 +229,8 @@ todo test the generic variants too new object[] { SerializerRanDataNode, - SerializerStruct.SerializerReturn, - SerializerStruct.SerializerReturnAlt, + (object)SerializerStruct.SerializerReturn, + (object)SerializerStruct.SerializerReturnAlt, false, new Func[] { @@ -241,8 +241,8 @@ todo test the generic variants too new object[] { SerializerRanCustomDataNode, - SerializerStruct.SerializerCustomReturn, - SerializerStruct.SerializerCustomReturnAlt, + (object)SerializerStruct.SerializerCustomReturn, + (object)SerializerStruct.SerializerCustomReturnAlt, true, new Func[] { diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/AngleSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/AngleSerializerTest.cs index 15c43c7f5f3..ae1c6b61c5c 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/AngleSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/AngleSerializerTest.cs @@ -5,8 +5,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ArraySerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ArraySerializerTest.cs index 33b0baf1e80..327c72783e3 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ArraySerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ArraySerializerTest.cs @@ -4,8 +4,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Box2SerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Box2SerializerTest.cs index e97480aaf88..9b44d8db01c 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Box2SerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Box2SerializerTest.cs @@ -4,8 +4,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ColorSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ColorSerializerTest.cs index b0708729904..798757bc2b2 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ColorSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ColorSerializerTest.cs @@ -4,8 +4,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs index 5dd8beac598..91ba30337ee 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ComponentRegistrySerializerTest.cs @@ -12,7 +12,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations; using YamlDotNet.RepresentationModel; using static Robust.Shared.Prototypes.EntityPrototype; -// ReSharper disable AccessToStaticMemberViaDerivedType namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/FlagSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/FlagSerializerTest.cs index f8360bd542b..30ac3bbb32a 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/FlagSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/FlagSerializerTest.cs @@ -8,8 +8,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers.Custom { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/Prototype/PrototypeIdListSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/Prototype/PrototypeIdListSerializerTest.cs index 5cd6e48fc73..027a874b687 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/Prototype/PrototypeIdListSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/Custom/Prototype/PrototypeIdListSerializerTest.cs @@ -14,8 +14,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using YamlDotNet.RepresentationModel; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers.Custom.Prototype { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/DictionarySerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/DictionarySerializerTest.cs index 8e41b29e9e7..d7a33d70353 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/DictionarySerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/DictionarySerializerTest.cs @@ -5,8 +5,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/FormattedMessageSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/FormattedMessageSerializerTest.cs index f8dd2b125e1..9ce2f797127 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/FormattedMessageSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/FormattedMessageSerializerTest.cs @@ -4,8 +4,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Utility; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers; [TestFixture] @@ -17,7 +15,7 @@ public sealed class FormattedMessageSerializerTest : SerializationTest [TestCase("[color=red]message[/color]")] public void SerializationTest(string text) { - var message = FormattedMessage.FromMarkup(text); + var message = FormattedMessage.FromMarkupOrThrow(text); var node = Serialization.WriteValueAs(message); Assert.That(node.Value, Is.EqualTo(text)); } diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/HashSetSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/HashSetSerializerTest.cs index 402c4c9cbc4..d53563c0a5b 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/HashSetSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/HashSetSerializerTest.cs @@ -5,8 +5,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/IntegerSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/IntegerSerializerTest.cs index 53563e69f3f..cf2b4fa05e2 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/IntegerSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/IntegerSerializerTest.cs @@ -1,8 +1,6 @@ using NUnit.Framework; using Robust.Shared.Serialization.Markdown.Value; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ListSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ListSerializerTest.cs index c2eb911ebc0..f792dc3a2e3 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ListSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/ListSerializerTest.cs @@ -5,8 +5,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/RegexSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/RegexSerializerTest.cs index 22e4c24b46e..5132e52df8c 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/RegexSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/RegexSerializerTest.cs @@ -4,8 +4,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/SortedSetSerializerTest.cs b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/SortedSetSerializerTest.cs index 917d4e2ee57..968eae3bf10 100644 --- a/Robust.UnitTesting/Shared/Serialization/TypeSerializers/SortedSetSerializerTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/TypeSerializers/SortedSetSerializerTest.cs @@ -5,8 +5,6 @@ using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers; [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/ImmutableListSerializationTest.cs b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/ImmutableListSerializationTest.cs index 33bb917f6f3..b908a8aef10 100644 --- a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/ImmutableListSerializationTest.cs +++ b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/ImmutableListSerializationTest.cs @@ -10,8 +10,6 @@ using YamlDotNet.Core; using YamlDotNet.RepresentationModel; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.YamlObjectSerializerTests { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypePropertySerialization_Test.cs b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypePropertySerialization_Test.cs index 3fc1988fc76..5b7b95732e2 100644 --- a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypePropertySerialization_Test.cs +++ b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypePropertySerialization_Test.cs @@ -10,8 +10,6 @@ using Robust.Shared.Serialization.Markdown.Value; using YamlDotNet.RepresentationModel; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.YamlObjectSerializerTests { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypeSerialization_Test.cs b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypeSerialization_Test.cs index 4ddbd894f07..5c87bbbcdeb 100644 --- a/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypeSerialization_Test.cs +++ b/Robust.UnitTesting/Shared/Serialization/YamlObjectSerializerTests/TypeSerialization_Test.cs @@ -7,8 +7,6 @@ using Robust.Shared.Serialization.Markdown.Mapping; using YamlDotNet.RepresentationModel; -// ReSharper disable AccessToStaticMemberViaDerivedType - namespace Robust.UnitTesting.Shared.Serialization.YamlObjectSerializerTests { [TestFixture] diff --git a/Robust.UnitTesting/Shared/Timing/GameLoop_Test.cs b/Robust.UnitTesting/Shared/Timing/GameLoop_Test.cs index db444941673..2bdc7ed2fb4 100644 --- a/Robust.UnitTesting/Shared/Timing/GameLoop_Test.cs +++ b/Robust.UnitTesting/Shared/Timing/GameLoop_Test.cs @@ -17,7 +17,6 @@ sealed class GameLoop_Test : RobustUnitTest /// With single step enabled, the game loop should run 1 tick and then pause again. /// [Test] - [Timeout(1000)] // comment this out if you want to debug public void SingleStepTest() { // TimeoutAttribute causes this to run on different thread on .NET Core, diff --git a/Robust.UnitTesting/Shared/Toolshed/ArithmeticTest.cs b/Robust.UnitTesting/Shared/Toolshed/ArithmeticTest.cs index 1eba5222348..1ec829f4a64 100644 --- a/Robust.UnitTesting/Shared/Toolshed/ArithmeticTest.cs +++ b/Robust.UnitTesting/Shared/Toolshed/ArithmeticTest.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Threading.Tasks; using NUnit.Framework; -using Is = NUnit.Framework.Is; namespace Robust.UnitTesting.Shared.Toolshed; @@ -16,9 +15,9 @@ public async Task OrderOfOperations() await Server.WaitAssertion(() => { // Toolshed always parses left-to-right with no precedence. - Assert.That(1.0f / 3.0f, Is.EqualTo(InvokeCommand("f 1 / 3"))); - Assert.That((1.0f + 1.0f) / 3.0f, Is.EqualTo(InvokeCommand("f 1 + 1 / 3"))); - Assert.That(float.Pow(2.0f + 2.0f, 3.0f), Is.EqualTo(InvokeCommand("f 2 + 2 pow 3"))); + Assert.That(InvokeCommand("f 1 / 3"), Is.EqualTo(1.0f / 3.0f)); + Assert.That(InvokeCommand("f 1 + 1 / 3"), Is.EqualTo((1.0f + 1.0f) / 3.0f)); + Assert.That(InvokeCommand("f 2 + 2 pow 3"), Is.EqualTo(float.Pow(2.0f + 2.0f, 3.0f))); }); } [Test] diff --git a/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs b/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs index e0c488c33fa..2e77af54433 100644 --- a/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs +++ b/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs @@ -32,10 +32,11 @@ public async Task TearDownInternal() Server.Dispose(); } - protected virtual async Task TearDown() + protected virtual Task TearDown() { Assert.That(_expectedErrors, Is.Empty); ClearErrors(); + return Task.CompletedTask; } [SetUp] diff --git a/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.BugCheck.cs b/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.BugCheck.cs index 972556c9a80..740d3f099fd 100644 --- a/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.BugCheck.cs +++ b/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.BugCheck.cs @@ -8,7 +8,7 @@ public sealed partial class ToolshedTypesTest { // Assert that T -> Nullable holds and it's inverse does not. [Test] - public async Task Bug_Nullables_08_21_2023() + public void Bug_Nullables_08_21_2023() { Assert.That(Toolshed.IsTransformableTo(typeof(int), typeof(Nullable))); Assert.That(!Toolshed.IsTransformableTo(typeof(Nullable), typeof(int))); diff --git a/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.cs b/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.cs index f72f3ba5922..0da055b21ad 100644 --- a/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.cs +++ b/Robust.UnitTesting/Shared/Toolshed/ToolshedTypesTest.cs @@ -11,7 +11,7 @@ public sealed partial class ToolshedTypesTest : ToolshedTest { // Assert that T -> IEnumerable holds. [Test] - public async Task EnumerableAutoCast() + public void EnumerableAutoCast() { Assert.That(Toolshed.IsTransformableTo(typeof(int), typeof(IEnumerable))); var l = Expression @@ -24,7 +24,7 @@ public async Task EnumerableAutoCast() // Assert that T -> IEnumerable where T: T' holds. [Test] - public async Task EnumerableSubtypeAutocast() + public void EnumerableSubtypeAutocast() { Assert.That(Toolshed.IsTransformableTo(typeof(int), typeof(IEnumerable))); var l = Expression @@ -37,7 +37,7 @@ public async Task EnumerableSubtypeAutocast() // Assert that T -> object. [Test] - public async Task CastToObject() + public void CastToObject() { Assert.That(Toolshed.IsTransformableTo(typeof(int), typeof(object))); var l = Expression diff --git a/Robust.UnitTesting/Shared/TransformTests/GridTraversalTest.cs b/Robust.UnitTesting/Shared/TransformTests/GridTraversalTest.cs index e9edf633c38..451c75b3e80 100644 --- a/Robust.UnitTesting/Shared/TransformTests/GridTraversalTest.cs +++ b/Robust.UnitTesting/Shared/TransformTests/GridTraversalTest.cs @@ -33,7 +33,7 @@ await server.WaitPost(() => grid = gridComp.Owner; mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1)); var gridCentre = new EntityCoordinates(grid, .5f, .5f); - gridMapPos = gridCentre.ToMap(sEntMan, xforms).Position; + gridMapPos = xforms.ToMapCoordinates(gridCentre).Position; }); for (int i = 0; i < 10; i++) diff --git a/Robust.UnitTesting/Shared/Utility/FormattedMessage_Test.cs b/Robust.UnitTesting/Shared/Utility/FormattedMessage_Test.cs index 0af1da290cc..cf22363d83c 100644 --- a/Robust.UnitTesting/Shared/Utility/FormattedMessage_Test.cs +++ b/Robust.UnitTesting/Shared/Utility/FormattedMessage_Test.cs @@ -13,7 +13,7 @@ public sealed class FormattedMessage_Test [Test] public static void TestParseMarkup() { - var msg = FormattedMessage.FromMarkup("foo[color=#aabbcc]bar[/color]baz"); + var msg = FormattedMessage.FromMarkupOrThrow("foo[color=#aabbcc]bar[/color]baz"); Assert.That(msg.Nodes, NUnit.Framework.Is.EquivalentTo(new MarkupNode[] { @@ -28,7 +28,7 @@ public static void TestParseMarkup() [Test] public static void TestParseMarkupColorName() { - var msg = FormattedMessage.FromMarkup("foo[color=orange]bar[/color]baz"); + var msg = FormattedMessage.FromMarkupOrThrow("foo[color=orange]bar[/color]baz"); Assert.That(msg.Nodes, NUnit.Framework.Is.EquivalentTo(new MarkupNode[] { @@ -57,7 +57,7 @@ public static void TestParsePermissiveMarkup(string text) [TestCase("[color=red]Foo[/color]bar", ExpectedResult = "Foobar")] public string TestRemoveMarkup(string test) { - return FormattedMessage.RemoveMarkup(test); + return FormattedMessage.RemoveMarkupOrThrow(test); } [Test] @@ -66,7 +66,7 @@ public string TestRemoveMarkup(string test) [TestCase("[color=lime]Foo[/color]bar")] public static void TestToMarkup(string text) { - var message = FormattedMessage.FromMarkup(text); + var message = FormattedMessage.FromMarkupOrThrow(text); Assert.That(message.ToMarkup(), NUnit.Framework.Is.EqualTo(text)); } @@ -77,7 +77,7 @@ public static void TestToMarkup(string text) [TestCase("honk honk [color=#00FF00FF]Foo[/color]bar")] public static void TestEnumerateRunes(string text) { - var message = FormattedMessage.FromMarkup(text); + var message = FormattedMessage.FromMarkupOrThrow(text); Assert.That( message.EnumerateRunes(), diff --git a/Robust.UnitTesting/Shared/Utility/MarkupNodeTest.cs b/Robust.UnitTesting/Shared/Utility/MarkupNodeTest.cs new file mode 100644 index 00000000000..b65ea186c75 --- /dev/null +++ b/Robust.UnitTesting/Shared/Utility/MarkupNodeTest.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using NUnit.Framework; +using Robust.Shared.Maths; +using Robust.Shared.Utility; + +namespace Robust.UnitTesting.Shared.Utility; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +[TestOf(typeof(MarkupNode))] +public sealed class MarkupNodeTest +{ + [Test] + [SuppressMessage("Assertion", "NUnit2009:The same value has been provided as both the actual and the expected argument")] + public void TestEquality() + { + Assert.Multiple(() => + { + // Test name match + Assert.That(new MarkupNode("A"), Is.EqualTo(new MarkupNode("A"))); + Assert.That(new MarkupNode("A").GetHashCode(), Is.EqualTo(new MarkupNode("A").GetHashCode())); + Assert.That(new MarkupNode("A"), Is.Not.EqualTo(new MarkupNode("B"))); + + // Test closing match + Assert.That(new MarkupNode("A", null, null, true), Is.EqualTo(new MarkupNode("A", null, null, true))); + Assert.That(new MarkupNode("A", null, null, true).GetHashCode(), Is.EqualTo(new MarkupNode("A", null, null, true).GetHashCode())); + Assert.That(new MarkupNode("A", null, null, true), Is.Not.EqualTo(new MarkupNode("A"))); + + // Test value match + var param = new MarkupParameter("A"); + Assert.That(new MarkupNode("A", param, null), Is.EqualTo(new MarkupNode("A", param, null))); + Assert.That(new MarkupNode("A", param, null).GetHashCode(), Is.EqualTo(new MarkupNode("A", param, null).GetHashCode())); + Assert.That(new MarkupNode("A", param, null), Is.Not.EqualTo(new MarkupNode("A"))); + Assert.That(new MarkupNode("A", param, null), Is.Not.EqualTo(new MarkupNode("A", new MarkupParameter("B"), null))); + + // Test attributes match + var attrs = new Dictionary + { + { "A", new MarkupParameter("A") }, + { "B", new MarkupParameter(5) }, + { "C", new MarkupParameter(Color.Red) }, + }; + var wrongAttrs = new Dictionary + { + { "A", new MarkupParameter("A") }, + { "B", new MarkupParameter(6) }, + { "C", new MarkupParameter(Color.Red) }, + }; + var wrongAttrsTooLong = new Dictionary + { + { "A", new MarkupParameter("A") }, + { "B", new MarkupParameter(5) }, + { "C", new MarkupParameter(Color.Red) }, + { "D", new MarkupParameter(Color.Red) }, + }; + var attrs2 = attrs.Reverse().ToDictionary(); + Assert.That(new MarkupNode("A", null, attrs), Is.EqualTo(new MarkupNode("A", null, attrs2))); + Assert.That(new MarkupNode("A", null, attrs).GetHashCode(), Is.EqualTo(new MarkupNode("A", null, attrs2).GetHashCode())); + Assert.That(new MarkupNode("A", null, attrs), Is.Not.EqualTo(new MarkupNode("A"))); + Assert.That(new MarkupNode("A", null, attrs), Is.Not.EqualTo(new MarkupNode("A", null, wrongAttrs))); + Assert.That(new MarkupNode("A", null, attrs), Is.Not.EqualTo(new MarkupNode("A", null, wrongAttrsTooLong))); + }); + } +} diff --git a/Robust.UnitTesting/Shared/Utility/ResPathTest.cs b/Robust.UnitTesting/Shared/Utility/ResPathTest.cs index e007730b585..62e014ee25a 100644 --- a/Robust.UnitTesting/Shared/Utility/ResPathTest.cs +++ b/Robust.UnitTesting/Shared/Utility/ResPathTest.cs @@ -2,9 +2,6 @@ using NUnit.Framework; using Robust.Shared.Utility; -// ReSharper disable AccessToStaticMemberViaDerivedType - - namespace Robust.UnitTesting.Shared.Utility; [TestFixture] diff --git a/Robust.UnitTesting/Shared/Utility/TextRope_Test.cs b/Robust.UnitTesting/Shared/Utility/TextRope_Test.cs index aad8130e6c3..1902ddd5e68 100644 --- a/Robust.UnitTesting/Shared/Utility/TextRope_Test.cs +++ b/Robust.UnitTesting/Shared/Utility/TextRope_Test.cs @@ -8,7 +8,6 @@ namespace Robust.UnitTesting.Shared.Utility; [TestFixture] [TestOf(typeof(Rope))] [Parallelizable(ParallelScope.All)] -[SuppressMessage("ReSharper", "AccessToStaticMemberViaDerivedType")] public static class TextRope_Test { [Test] diff --git a/Robust.UnitTesting/TestingParallelManager.cs b/Robust.UnitTesting/TestingParallelManager.cs index 2434514f25a..0a88eff86df 100644 --- a/Robust.UnitTesting/TestingParallelManager.cs +++ b/Robust.UnitTesting/TestingParallelManager.cs @@ -9,7 +9,7 @@ namespace Robust.UnitTesting; /// j public sealed class TestingParallelManager : IParallelManagerInternal { - public event Action? ParallelCountChanged; + public event Action? ParallelCountChanged { add { } remove { } } public int ParallelProcessCount => 1; public void AddAndInvokeParallelCountChanged(Action changed) diff --git a/RobustToolbox.sln b/RobustToolbox.sln index 43580afda26..5eefb5aa730 100644 --- a/RobustToolbox.sln +++ b/RobustToolbox.sln @@ -53,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cefglue", "cefglue", "{2D78 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CefGlue", "cefglue\CefGlue\CefGlue.csproj", "{6BC71226-BA9C-4CD6-9838-03AC076F9518}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Xaml", "Robust.Xaml\Robust.Xaml.csproj", "{EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -229,6 +231,14 @@ Global {6BC71226-BA9C-4CD6-9838-03AC076F9518}.Release|Any CPU.Build.0 = Release|Any CPU {6BC71226-BA9C-4CD6-9838-03AC076F9518}.Release|x64.ActiveCfg = Release|Any CPU {6BC71226-BA9C-4CD6-9838-03AC076F9518}.Release|x64.Build.0 = Release|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Debug|x64.ActiveCfg = Debug|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Debug|x64.Build.0 = Debug|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Release|Any CPU.Build.0 = Release|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Release|x64.ActiveCfg = Release|Any CPU + {EC7BA4C0-A02F-40E8-B4FC-9A96D91BD1EC}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tools/version.py b/Tools/version.py index a8e2d0c8f35..b12f18278f5 100755 --- a/Tools/version.py +++ b/Tools/version.py @@ -28,7 +28,7 @@ def main(): def verify_version(version: str): - parts = version.split(".") + parts = version.split("-")[0].split(".") if len(parts) != 3: print("Version must be split into three parts with '.'") sys.exit(1)