From ebc0fc9c60c2d52c5620b5defb6695e00bc6ae5d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Wed, 1 Dec 2021 12:59:42 +1100 Subject: [PATCH] Move physics fixtures to its own component (#2220) --- .../Console/Commands/TestbedCommand.cs | 8 +- Robust.Server/Map/ServerMapManager.cs | 4 +- Robust.Server/Maps/MapLoader.cs | 14 +- .../Collidable/PhysicsComponent.Collision.cs | 13 - .../Collidable/PhysicsComponent.Physics.cs | 292 +------------ .../Collidable/PhysicsComponentState.cs | 5 - .../Collidable/PhysicsInitializedEvent.cs | 12 + .../Systems/SharedGridFixtureSystem.cs | 17 +- .../Systems/SharedPhysicsSystem.Queries.cs | 4 +- .../Physics/Collision/Shapes/EdgeShape.cs | 5 - .../Physics/Collision/Shapes/IPhysShape.cs | 2 - .../Physics/Collision/Shapes/PhysShapeAabb.cs | 9 - .../Collision/Shapes/PhysShapeCircle.cs | 3 - .../Physics/Collision/Shapes/PolygonShape.cs | 9 +- Robust.Shared/Physics/Dynamics/Fixture.cs | 60 +-- ...hapeManager.cs => FixtureSystem.Shapes.cs} | 12 +- Robust.Shared/Physics/FixtureSystem.cs | 410 ++++++++++++++++++ Robust.Shared/Physics/FixturesComponent.cs | 52 +++ Robust.Shared/Physics/IPhysBody.cs | 4 +- .../Physics/SharedBroadphaseSystem.cs | 155 ++----- Robust.Shared/SharedIoC.cs | 1 - Robust.UnitTesting/RobustUnitTest.cs | 10 + .../Server/Maps/MapLoaderTest.cs | 9 +- .../Server/RobustServerSimulation.cs | 2 + .../Systems/AnchoredSystemTests.cs | 4 - .../Shared/Physics/CollisionWake_Test.cs | 1 + ...peManager_Test.cs => FixtureShape_Test.cs} | 10 +- .../Shared/Physics/MapVelocity_Test.cs | 1 + .../Shared/Physics/PhysicsComponent_Test.cs | 6 +- .../Shared/Physics/Stack_Test.cs | 21 +- 30 files changed, 618 insertions(+), 537 deletions(-) create mode 100644 Robust.Shared/GameObjects/Components/Collidable/PhysicsInitializedEvent.cs rename Robust.Shared/Physics/{ShapeManager.cs => FixtureSystem.Shapes.cs} (94%) create mode 100644 Robust.Shared/Physics/FixtureSystem.cs create mode 100644 Robust.Shared/Physics/FixturesComponent.cs rename Robust.UnitTesting/Shared/Physics/{ShapeManager_Test.cs => FixtureShape_Test.cs} (89%) diff --git a/Robust.Server/Console/Commands/TestbedCommand.cs b/Robust.Server/Console/Commands/TestbedCommand.cs index 3a5d6bf93..121b8499e 100644 --- a/Robust.Server/Console/Commands/TestbedCommand.cs +++ b/Robust.Server/Console/Commands/TestbedCommand.cs @@ -130,7 +130,7 @@ namespace Robust.Server.Console.Commands Hard = true }; - var broadphase = EntitySystem.Get(); + var broadphase = EntitySystem.Get(); broadphase.CreateFixture(ground, horizontalFixture); @@ -194,7 +194,7 @@ namespace Robust.Server.Console.Commands Hard = true }; - var broadphase = EntitySystem.Get(); + var broadphase = EntitySystem.Get(); broadphase.CreateFixture(ground, horizontalFixture); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); @@ -258,7 +258,7 @@ namespace Robust.Server.Console.Commands Hard = true }; - var broadphase = EntitySystem.Get(); + var broadphase = EntitySystem.Get(); broadphase.CreateFixture(ground, horizontalFixture); // Setup boxes @@ -296,7 +296,7 @@ namespace Robust.Server.Console.Commands private void CreateTumbler(MapId mapId) { - var broadphaseSystem = EntitySystem.Get(); + var broadphaseSystem = EntitySystem.Get(); var entityManager = IoCManager.Resolve(); var groundUid = entityManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId)).Uid; diff --git a/Robust.Server/Map/ServerMapManager.cs b/Robust.Server/Map/ServerMapManager.cs index ddffa79c1..fee201a59 100644 --- a/Robust.Server/Map/ServerMapManager.cs +++ b/Robust.Server/Map/ServerMapManager.cs @@ -51,11 +51,11 @@ namespace Robust.Server.Map // TODO: Like MapManager injecting this is a PITA so need to work out an easy way to do it. // Maybe just add like a PostInject method that gets called way later? - var broadphaseSystem = EntitySystem.Get(); + var fixtureSystem = EntitySystem.Get(); foreach (var fixture in chunk.Fixtures) { - broadphaseSystem.DestroyFixture(body, fixture); + fixtureSystem.DestroyFixture(body, fixture); } } diff --git a/Robust.Server/Maps/MapLoader.cs b/Robust.Server/Maps/MapLoader.cs index c1c348144..8e9410f9d 100644 --- a/Robust.Server/Maps/MapLoader.cs +++ b/Robust.Server/Maps/MapLoader.cs @@ -441,19 +441,21 @@ namespace Robust.Server.Maps { var entManager = IoCManager.Resolve(); var gridFixtures = EntitySystem.Get(); - var broadphaseSystem = EntitySystem.Get(); + var fixtureSystem = EntitySystem.Get(); foreach (var grid in Grids) { var gridInternal = (IMapGridInternal) grid; - var body = entManager.EnsureComponent(entManager.GetEntity(grid.GridEntityId)); + var body = entManager.EnsureComponent(grid.GridEntityId); + body.Broadphase = _mapManager.GetMapEntity(grid.ParentMapId).GetComponent(); + var fixtures = entManager.EnsureComponent(grid.GridEntityId); gridFixtures.ProcessGrid(gridInternal); // Need to go through and double-check we don't have any hanging-on fixtures that // no longer apply (e.g. due to an update in GridFixtureSystem) - var toRemove = new List(); + var toRemove = new RemQueue(); - foreach (var fixture in body.Fixtures) + foreach (var (_, fixture) in fixtures.Fixtures) { var found = false; @@ -477,8 +479,10 @@ namespace Robust.Server.Maps foreach (var fixture in toRemove) { - broadphaseSystem.DestroyFixture(fixture); + fixtureSystem.DestroyFixture(body, fixture, false, fixtures); } + + fixtureSystem.FixtureUpdate(fixtures, body); } } diff --git a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Collision.cs b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Collision.cs index 5fa44d5a3..eb84c5cca 100644 --- a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Collision.cs +++ b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Collision.cs @@ -24,17 +24,4 @@ namespace Robust.Shared.GameObjects Component = component; } } - - public sealed class FixtureUpdateMessage : EntityEventArgs - { - public PhysicsComponent Body { get; } - - public Fixture Fixture { get; } - - public FixtureUpdateMessage(PhysicsComponent body, Fixture fixture) - { - Body = body; - Fixture = fixture; - } - } } diff --git a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Physics.cs b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Physics.cs index b89a86f2a..41892e35c 100644 --- a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Physics.cs +++ b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponent.Physics.cs @@ -28,13 +28,9 @@ using System.Linq; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Physics; -using Robust.Shared.Physics.Broadphase; -using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Dynamics.Contacts; using Robust.Shared.Players; @@ -77,6 +73,13 @@ namespace Robust.Shared.GameObjects public bool IgnoreCCD { get; set; } + // TODO: Placeholder; look it's disgusting but my main concern is stopping fixtures being serialized every tick + // on physics bodies for massive shuttle perf savings. + [Obsolete("Use FixturesComponent instead.")] + public IReadOnlyList Fixtures => Owner.EntityManager.GetComponent(OwnerUid).Fixtures.Values.ToList(); + + public int FixtureCount => Owner.EntityManager.GetComponent(OwnerUid).Fixtures.Count; + [ViewVariables] public int ContactCount { @@ -94,47 +97,6 @@ namespace Robust.Shared.GameObjects } } - [ViewVariables] - public Box2 LocalAABB - { - get - { - var broadphaseSystem = EntitySystem.Get(); - var broadphase = broadphaseSystem.GetBroadphase(this); - - if (broadphase == null) return new Box2(); - - var worldPos = Owner.Transform.WorldPosition; - var aabb = new Box2(worldPos, worldPos); - - foreach (var fixture in Fixtures) - { - foreach (var proxy in fixture.Proxies) - { - aabb = aabb.Union(proxy.AABB); - } - } - - return aabb; - } - } - - [ViewVariables] - public Box2 WorldAABB - { - get - { - var broadphaseSystem = EntitySystem.Get(); - var broadphase = broadphaseSystem.GetBroadphase(this); - - if (broadphase == null) return new Box2(); - var localAABB = LocalAABB; - var center = broadphase.Owner.Transform.WorldMatrix.Transform(localAABB.Center); - - return new Box2Rotated(localAABB.Translated(center), broadphase.Owner.Transform.WorldRotation, center).CalcBoundingBox(); - } - } - public IEnumerable Contacts { get @@ -294,29 +256,10 @@ namespace Robust.Shared.GameObjects Awake = true; } - void ISerializationHooks.AfterDeserialization() - { - FixtureCount = _fixtures.Count; - - foreach (var fixture in _fixtures) - { - fixture.Body = this; - fixture.ComputeProperties(); - fixture.ID = GetFixtureName(fixture); - } - - ResetMassData(); - - if (_mass > 0f && (BodyType & (BodyType.Dynamic | BodyType.KinematicController)) != 0) - { - _invMass = 1.0f / _mass; - } - } - /// public override ComponentState GetComponentState() { - return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _fixtures, LinearVelocity, AngularVelocity, BodyType); + return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _linVelocity, _angVelocity, _bodyType); } /// @@ -333,95 +276,7 @@ namespace Robust.Shared.GameObjects // So transform doesn't apply MapId in the HandleComponentState because ??? so MapId can still be 0. // Fucking kill me, please. You have no idea deep the rabbit hole of shitcode goes to make this work. - /* - * -- Fixtures -- - */ - - var toAddFixtures = new List(); - var toRemoveFixtures = new List(); - var computeProperties = false; - - // Given a bunch of data isn't serialized need to sort of re-initialise it - var newFixtures = new List(newState.Fixtures.Count); - foreach (var fixture in newState.Fixtures) - { - var newFixture = new Fixture(); - fixture.CopyTo(newFixture); - newFixture.Body = this; - newFixtures.Add(newFixture); - } - - // Add / update new fixtures - foreach (var fixture in newFixtures) - { - var found = false; - - foreach (var existing in _fixtures) - { - if (!fixture.ID.Equals(existing.ID)) continue; - - if (!fixture.Equals(existing)) - { - toAddFixtures.Add(fixture); - toRemoveFixtures.Add(existing); - } - - found = true; - break; - } - - if (!found) - { - toAddFixtures.Add(fixture); - } - } - - // Remove old fixtures - foreach (var existing in _fixtures) - { - var found = false; - - foreach (var fixture in newFixtures) - { - if (fixture.ID.Equals(existing.ID)) - { - found = true; - break; - } - } - - if (!found) - { - toRemoveFixtures.Add(existing); - } - } - - var broadphaseSystem = EntitySystem.Get(); - - foreach (var fixture in toRemoveFixtures) - { - computeProperties = true; - broadphaseSystem.DestroyFixture(this, fixture); - } - - // TODO: We also still need event listeners for shapes (Probably need C# events) - foreach (var fixture in toAddFixtures) - { - computeProperties = true; - broadphaseSystem.CreateFixture(this, fixture); - fixture.Shape.ApplyState(); - } - - /* - * -- Sundries -- - */ - Dirty(); - if (computeProperties) - { - ResetMassData(); - } - LinearVelocity = newState.LinearVelocity; // Logger.Debug($"{IGameTiming.TickStampStatic}: [{Owner}] {LinearVelocity}"); AngularVelocity = newState.AngularVelocity; @@ -429,24 +284,6 @@ namespace Robust.Shared.GameObjects Predict = false; } - public Fixture? GetFixture(string name) - { - // Sooo I'd rather have fixtures as a list in serialization but there's not really an easy way to have it as a - // temporary value on deserialization so we can store it as a dictionary of - // given 100% of bodies have 1-2 fixtures this isn't really a performance problem right now but - // should probably be done at some stage - // If we really need it then you just deserialize onto a dummy field that then just never gets used again. - foreach (var fixture in _fixtures) - { - if (fixture.ID.Equals(name)) - { - return fixture; - } - } - - return null; - } - /// /// Resets the dynamics of this body. /// Sets torque, force and linear/angular velocity to 0 @@ -468,7 +305,7 @@ namespace Robust.Shared.GameObjects var bounds = new Box2(transform.Position, transform.Position); - foreach (var fixture in _fixtures) + foreach (var fixture in Fixtures) { for (var i = 0; i < fixture.Shape.ChildCount; i++) { @@ -480,17 +317,6 @@ namespace Robust.Shared.GameObjects return bounds; } - [ViewVariables] - public int FixtureCount { get; internal set; } - - /// - [ViewVariables] - public IReadOnlyList Fixtures => _fixtures; - - [DataField("fixtures")] - [NeverPushInheritance] - internal List _fixtures = new(); - /// /// Enables or disabled collision processing of this component. /// @@ -525,57 +351,19 @@ namespace Robust.Shared.GameObjects /// This is useful for triggers or such to detect collision without actually causing a blockage. /// [ViewVariables(VVAccess.ReadWrite)] - public bool Hard - { - get - { - foreach (var fixture in Fixtures) - { - if (fixture.Hard) return true; - } - - return false; - } - set - { - foreach (var fixture in Fixtures) - { - fixture.Hard = value; - } - } - } + public bool Hard { get; internal set; } /// /// Bitmask of the collision layers this component is a part of. /// - [ViewVariables(VVAccess.ReadWrite)] - public int CollisionLayer - { - get - { - var layers = 0x0; - - foreach (var fixture in Fixtures) - layers |= fixture.CollisionLayer; - return layers; - } - } + [ViewVariables] + public int CollisionLayer { get; internal set; } /// /// Bitmask of the layers this component collides with. /// - [ViewVariables(VVAccess.ReadWrite)] - public int CollisionMask - { - get - { - var mask = 0x0; - - foreach (var fixture in Fixtures) - mask |= fixture.CollisionMask; - return mask; - } - } + [ViewVariables] + public int CollisionMask { get; internal set; } // I made Mass read-only just because overwriting it doesn't touch inertia. /// @@ -973,41 +761,6 @@ namespace Robust.Shared.GameObjects return Transform.MulT(new Quaternion2D((float) Owner.EntityManager.GetComponent(OwnerUid).WorldRotation.Theta), worldVector); } - public void FixtureChanged(Fixture fixture) - { - // TODO: Optimise this a LOT - Dirty(); - Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new FixtureUpdateMessage(this, fixture)); - } - - public string GetFixtureName(Fixture fixture) - { - if (!string.IsNullOrEmpty(fixture.ID)) return fixture.ID; - - var i = 0; - - while (true) - { - var found = false; - ++i; - var name = $"fixture_{i}"; - - foreach (var existing in _fixtures) - { - if (existing.ID.Equals(name)) - { - found = true; - break; - } - } - - if (!found) - { - return name; - } - } - } - public Transform GetTransform() { var (worldPos, worldRot) = Owner.Transform.GetWorldPositionRotation(); @@ -1121,16 +874,11 @@ namespace Robust.Shared.GameObjects { _awake = false; } - } - protected override void OnRemove() - { - base.OnRemove(); - // Need to do these immediately in case collision behaviors deleted the body - // TODO: Could be more optimal as currently broadphase will call this ANYWAY - DestroyContacts(); - EntitySystem.Get().RemoveBody(this); - CanCollide = false; + var startup = new PhysicsInitializedEvent(Owner.Uid); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ref startup); + + ResetMassData(); } public void ResetMassData() @@ -1147,9 +895,9 @@ namespace Robust.Shared.GameObjects } var localCenter = Vector2.Zero; - var shapeManager = IoCManager.Resolve(); + var shapeManager = EntitySystem.Get(); - foreach (var fixture in _fixtures) + foreach (var fixture in Fixtures) { if (fixture.Mass <= 0.0f) continue; diff --git a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponentState.cs b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponentState.cs index eb23158ad..5a68004ad 100644 --- a/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponentState.cs +++ b/Robust.Shared/GameObjects/Components/Collidable/PhysicsComponentState.cs @@ -15,7 +15,6 @@ namespace Robust.Shared.GameObjects public readonly bool SleepingAllowed; public readonly bool FixedRotation; public readonly BodyStatus Status; - public readonly List Fixtures; public readonly Vector2 LinearVelocity; public readonly float AngularVelocity; @@ -28,8 +27,6 @@ namespace Robust.Shared.GameObjects /// /// /// - /// - /// /// Current linear velocity of the entity in meters per second. /// Current angular velocity of the entity in radians per sec. /// @@ -38,7 +35,6 @@ namespace Robust.Shared.GameObjects bool sleepingAllowed, bool fixedRotation, BodyStatus status, - List fixtures, Vector2 linearVelocity, float angularVelocity, BodyType bodyType) @@ -47,7 +43,6 @@ namespace Robust.Shared.GameObjects SleepingAllowed = sleepingAllowed; FixedRotation = fixedRotation; Status = status; - Fixtures = fixtures; LinearVelocity = linearVelocity; AngularVelocity = angularVelocity; diff --git a/Robust.Shared/GameObjects/Components/Collidable/PhysicsInitializedEvent.cs b/Robust.Shared/GameObjects/Components/Collidable/PhysicsInitializedEvent.cs new file mode 100644 index 000000000..3e0e68b23 --- /dev/null +++ b/Robust.Shared/GameObjects/Components/Collidable/PhysicsInitializedEvent.cs @@ -0,0 +1,12 @@ +namespace Robust.Shared.GameObjects +{ + public readonly struct PhysicsInitializedEvent + { + public readonly EntityUid Uid; + + public PhysicsInitializedEvent(EntityUid uid) + { + Uid = uid; + } + } +} diff --git a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs index 86e798985..fc0b21f68 100644 --- a/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedGridFixtureSystem.cs @@ -16,7 +16,7 @@ namespace Robust.Shared.GameObjects public abstract class SharedGridFixtureSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; + [Dependency] private readonly FixtureSystem _fixtures = default!; private bool _enabled; @@ -59,6 +59,12 @@ namespace Robust.Shared.GameObjects return; } + if (!gridEnt.TryGetComponent(out FixturesComponent? fixturesComponent)) + { + Logger.ErrorS("physics", $"Trying to regenerate collision for {gridEnt} that doesn't have {nameof(fixturesComponent)}"); + return; + } + var origin = chunk.Indices * chunk.ChunkSize; // So we store a reference to the fixture on the chunk because it's easier to cross-reference it. @@ -123,7 +129,7 @@ namespace Robust.Shared.GameObjects foreach (var fixture in toRemove) { chunk.Fixtures.Remove(fixture); - _broadphase.DestroyFixture(fixture); + _fixtures.DestroyFixture(fixture, false, fixturesComponent); } if (newFixtures.Count > 0 || toRemove.List?.Count > 0) @@ -134,7 +140,7 @@ namespace Robust.Shared.GameObjects // Anything remaining is a new fixture (or at least, may have not serialized onto the chunk yet). foreach (var fixture in newFixtures) { - var existingFixture = physicsComponent.GetFixture(fixture.ID); + var existingFixture = _fixtures.GetFixtureOrNull(physicsComponent, fixture.ID); // Check if it's the same (otherwise remove anyway). if (existingFixture?.Shape is PolygonShape poly && poly.EqualsApprox((PolygonShape) fixture.Shape)) @@ -144,11 +150,14 @@ namespace Robust.Shared.GameObjects } chunk.Fixtures.Add(fixture); - _broadphase.CreateFixture(physicsComponent, fixture); + _fixtures.CreateFixture(physicsComponent, fixture, false, fixturesComponent); } if (updated) + { + _fixtures.FixtureUpdate(fixturesComponent, physicsComponent); EntityManager.EventBus.RaiseLocalEvent(gridEnt.Uid,new GridFixtureChangeEvent {NewFixtures = chunk.Fixtures}); + } } } diff --git a/Robust.Shared/GameObjects/Systems/SharedPhysicsSystem.Queries.cs b/Robust.Shared/GameObjects/Systems/SharedPhysicsSystem.Queries.cs index 390aac7b5..5076a3eac 100644 --- a/Robust.Shared/GameObjects/Systems/SharedPhysicsSystem.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/SharedPhysicsSystem.Queries.cs @@ -73,7 +73,7 @@ namespace Robust.Shared.GameObjects var state = (body, entities); - foreach (var fixture in body._fixtures) + foreach (var fixture in body.Fixtures) { foreach (var proxy in fixture.Proxies) { @@ -165,7 +165,7 @@ namespace Robust.Shared.GameObjects var localTransform = new Transform(broadInvMatrix.Transform(transform.Position), transform.Quaternion2D.Angle - broadRot); - foreach (var fixture in body._fixtures) + foreach (var fixture in body.Fixtures) { var collisionMask = fixture.CollisionMask; var collisionLayer = fixture.CollisionLayer; diff --git a/Robust.Shared/Physics/Collision/Shapes/EdgeShape.cs b/Robust.Shared/Physics/Collision/Shapes/EdgeShape.cs index da35b7a30..4b735055b 100644 --- a/Robust.Shared/Physics/Collision/Shapes/EdgeShape.cs +++ b/Robust.Shared/Physics/Collision/Shapes/EdgeShape.cs @@ -160,10 +160,5 @@ namespace Robust.Shared.Physics.Collision.Shapes // It's a line return 0f; } - - public void ApplyState() - { - return; - } } } diff --git a/Robust.Shared/Physics/Collision/Shapes/IPhysShape.cs b/Robust.Shared/Physics/Collision/Shapes/IPhysShape.cs index 76ec0aa97..65628998d 100644 --- a/Robust.Shared/Physics/Collision/Shapes/IPhysShape.cs +++ b/Robust.Shared/Physics/Collision/Shapes/IPhysShape.cs @@ -39,7 +39,5 @@ namespace Robust.Shared.Physics.Collision.Shapes /// Calculate the AABB of the shape. /// Box2 ComputeAABB(Transform transform, int childIndex); - - void ApplyState(); } } diff --git a/Robust.Shared/Physics/Collision/Shapes/PhysShapeAabb.cs b/Robust.Shared/Physics/Collision/Shapes/PhysShapeAabb.cs index 9c753f1f7..d25c35e87 100644 --- a/Robust.Shared/Physics/Collision/Shapes/PhysShapeAabb.cs +++ b/Robust.Shared/Physics/Collision/Shapes/PhysShapeAabb.cs @@ -32,7 +32,6 @@ namespace Robust.Shared.Physics.Collision.Shapes { if (MathHelper.CloseToPercent(_radius, value)) return; _radius = value; - OnDataChanged?.Invoke(); } } @@ -58,7 +57,6 @@ namespace Robust.Shared.Physics.Collision.Shapes return; _localBounds = value; - OnDataChanged?.Invoke(); } } @@ -77,13 +75,6 @@ namespace Robust.Shared.Physics.Collision.Shapes return new Box2Rotated(_localBounds.Translated(transform.Position), transform.Quaternion2D.Angle, transform.Position).CalcBoundingBox().Enlarged(_radius); } - /// - public void ApplyState() { } - - // TODO - [field: NonSerialized] - public event Action? OnDataChanged; - [Pure] internal List GetVertices() { diff --git a/Robust.Shared/Physics/Collision/Shapes/PhysShapeCircle.cs b/Robust.Shared/Physics/Collision/Shapes/PhysShapeCircle.cs index 5126e8b3b..baa7504fc 100644 --- a/Robust.Shared/Physics/Collision/Shapes/PhysShapeCircle.cs +++ b/Robust.Shared/Physics/Collision/Shapes/PhysShapeCircle.cs @@ -72,9 +72,6 @@ namespace Robust.Shared.Physics.Collision.Shapes return new Box2(p.X - _radius, p.Y - _radius, p.X + _radius, p.Y + _radius); } - /// - public void ApplyState() { } - public bool Equals(IPhysShape? other) { if (other is not PhysShapeCircle otherCircle) return false; diff --git a/Robust.Shared/Physics/Collision/Shapes/PolygonShape.cs b/Robust.Shared/Physics/Collision/Shapes/PolygonShape.cs index 3902548b3..463ce3720 100644 --- a/Robust.Shared/Physics/Collision/Shapes/PolygonShape.cs +++ b/Robust.Shared/Physics/Collision/Shapes/PolygonShape.cs @@ -128,9 +128,7 @@ namespace Robust.Shared.Physics.Collision.Shapes Normals[i] = temp.Normalized; } - // Compute the polygon mass data - // TODO: Update fixture. Maybe use events for it? Who tf knows. - // If we get grid polys then we'll actually need runtime updating of bbs. + // TODO: Updates (network etc) } public ShapeType ShapeType => ShapeType.Polygon; @@ -247,11 +245,6 @@ namespace Robust.Shared.Physics.Collision.Shapes return new Box2(lower - r, upper + r); } - public void ApplyState() - { - return; - } - public static explicit operator PolygonShape(PhysShapeAabb aabb) { // TODO: Need a test for this probably, if there is no AABB manifold generator done at least. diff --git a/Robust.Shared/Physics/Dynamics/Fixture.cs b/Robust.Shared/Physics/Dynamics/Fixture.cs index a605fe8ed..a1d1a99aa 100644 --- a/Robust.Shared/Physics/Dynamics/Fixture.cs +++ b/Robust.Shared/Physics/Dynamics/Fixture.cs @@ -82,7 +82,8 @@ namespace Robust.Shared.Physics.Dynamics if (MathHelper.CloseToPercent(value, _friction)) return; _friction = value; - Body.FixtureChanged(this); + // TODO: EntitySystem + // Body.FixtureChanged(this); } } @@ -99,10 +100,11 @@ namespace Robust.Shared.Physics.Dynamics get => _restitution; set { - if (MathHelper.CloseToPercent(value, _restitution)) return; + if (MathHelper.CloseTo(value, _restitution)) return; + // TODO: EntitySystem _restitution = value; - Body.FixtureChanged(this); + // Body.FixtureChanged(this); } } @@ -125,9 +127,10 @@ namespace Robust.Shared.Physics.Dynamics if (_hard == value) return; + // TODO: EntitySystem _hard = value; Body.Awake = true; - Body.FixtureChanged(this); + // Body.FixtureChanged(this); } } @@ -153,9 +156,9 @@ namespace Robust.Shared.Physics.Dynamics { if (MathHelper.CloseToPercent(value, _mass)) return; + // TODO: EntitySystem _mass = MathF.Max(0f, value); - ComputeProperties(); - Body.FixtureChanged(this); + // Body.FixtureChanged(this); Body.ResetMassData(); } } @@ -175,8 +178,9 @@ namespace Robust.Shared.Physics.Dynamics if (_collisionLayer == value) return; + // TODO: EntitySystem _collisionLayer = value; - Body.FixtureChanged(this); + // Body.FixtureChanged(this); EntitySystem.Get().Refilter(this); } } @@ -196,8 +200,9 @@ namespace Robust.Shared.Physics.Dynamics if (_collisionMask == value) return; + // TODO: EntitySystem _collisionMask = value; - Body.FixtureChanged(this); + // Body.FixtureChanged(this); EntitySystem.Get().Refilter(this); } } @@ -276,47 +281,8 @@ namespace Robust.Shared.Physics.Dynamics fixture._mass = _mass; } - // Moved from Shape because no MassData on Shape anymore (due to serv3 and physics ease-of-use etc etc.) - internal void ComputeProperties() - { - switch (Shape) - { - case EdgeShape edge: - ComputeEdge(edge); - break; - case PhysShapeAabb aabb: - ComputeAABB(aabb); - break; - case PhysShapeCircle circle: - ComputeCircle(circle); - break; - case PolygonShape poly: - ComputePoly(poly, out _); - break; - default: - throw new NotImplementedException(); - } - } - #region ComputeProperties - private void ComputeAABB(PhysShapeAabb aabb) - { - var area = aabb.LocalBounds.Width * aabb.LocalBounds.Height; - float I = 0.0f; - //The area is too small for the engine to handle. - DebugTools.Assert(area > float.Epsilon); - - // Total mass - // TODO: Do we need this? - var density = area > 0.0f ? Mass / area : 0.0f; - - // Center of mass - aabb.Centroid = Vector2.Zero; - - // Inertia tensor relative to the local origin (point s). - _inertia = density * I; - } private void ComputePoly(PolygonShape poly, out float area) { diff --git a/Robust.Shared/Physics/ShapeManager.cs b/Robust.Shared/Physics/FixtureSystem.Shapes.cs similarity index 94% rename from Robust.Shared/Physics/ShapeManager.cs rename to Robust.Shared/Physics/FixtureSystem.Shapes.cs index fe40b5fba..87776843e 100644 --- a/Robust.Shared/Physics/ShapeManager.cs +++ b/Robust.Shared/Physics/FixtureSystem.Shapes.cs @@ -6,19 +6,11 @@ using Robust.Shared.Utility; namespace Robust.Shared.Physics { - public interface IShapeManager + public partial class FixtureSystem { /// - /// Returns whether a particular point intersects the specified shape. + /// Tests whether a particular point is contained in the shape. /// - bool TestPoint(IPhysShape shape, Transform xform, Vector2 worldPoint); - - void GetMassData(IPhysShape shape, ref MassData data); - } - - public class ShapeManager : IShapeManager - { - /// public bool TestPoint(IPhysShape shape, Transform xform, Vector2 worldPoint) { switch (shape) diff --git a/Robust.Shared/Physics/FixtureSystem.cs b/Robust.Shared/Physics/FixtureSystem.cs new file mode 100644 index 000000000..778ee5ffa --- /dev/null +++ b/Robust.Shared/Physics/FixtureSystem.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Robust.Shared.Physics +{ + /// + /// Manages physics fixtures. + /// + public sealed partial class FixtureSystem : EntitySystem + { + [Dependency] private readonly SharedBroadphaseSystem _broadphaseSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnPhysicsInit); + SubscribeLocalEvent(OnPhysicsShutdown); + } + + private void OnShutdown(EntityUid uid, FixturesComponent component, ComponentShutdown args) + { + // TODO: Need a better solution to this because the only reason I don't throw is that allcomponents test + // Yes it is actively making the game buggier but I would essentially double the size of this PR trying to fix it + // my best solution rn is move the broadphase property onto FixturesComponent and then refactor + // SharedBroadphaseSystem a LOT. + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? body)) + { + return; + } + + // Can't just get physicscomp on shutdown as it may be touched completely independently. + body.DestroyContacts(); + _broadphaseSystem.RemoveBody(body, component); + body.CanCollide = false; + } + + private void OnInit(EntityUid uid, FixturesComponent component, ComponentInit args) + { + // Convert the serialized list to the dictionary format as it may not necessarily have an ID in YAML + // (probably change this someday for perf reasons?) + foreach (var fixture in component.SerializedFixtures) + { + fixture.ID = GetFixtureName(component, fixture); + + if (component.Fixtures.TryAdd(fixture.ID, fixture)) continue; + + Logger.DebugS("physics", $"Tried to deserialize fixture {fixture.ID} on {uid} which already exists."); + } + + component.SerializedFixtures.Clear(); + + if (component.Fixtures.Count <= 0 || + !EntityManager.TryGetComponent(uid, out PhysicsComponent? body) || + !EntityManager.TryGetComponent(uid, out TransformComponent? xform)) return; + + // Ordering issues man + var broadphase = _broadphaseSystem.GetBroadphase(body); + body.Broadphase = broadphase; + + if (broadphase != null) + { + var worldPos = xform.WorldPosition; + var worldRot = xform.WorldRotation; + + // Can't resolve in serialization so here we are. + // TODO: Support for large body DynamicTrees (i.e. 1 proxy for the entire body) + foreach (var (_, fixture) in component.Fixtures) + { + // It's possible that fixtures were added at some stage prior to this so we'll just check if they're on the broadphase + if (fixture.ProxyCount > 0) continue; + + fixture.Body = body; + + _broadphaseSystem.CreateProxies(fixture, worldPos, worldRot, false); + } + } + + // Make sure all the right stuff is set on the body + FixtureUpdate(component); + } + + #region Public + + public void CreateFixture(PhysicsComponent body, Fixture fixture, bool updates = true, FixturesComponent? manager = null, TransformComponent? xform = null) + { + if (!Resolve(body.OwnerUid, ref manager, ref xform)) + { + DebugTools.Assert(false); + return; + } + + fixture.ID = GetFixtureName(manager, fixture); + manager.Fixtures.Add(fixture.ID, fixture); + fixture.Body = body; + + // TODO: Assert world locked + // Broadphase should be set in the future TM + // Should only happen for nullspace / initializing entities + if (body.Broadphase != null) + { + var worldPos = xform.WorldPosition; + var worldRot = xform.WorldRotation; + + _broadphaseSystem.UpdateBroadphaseCache(body.Broadphase); + _broadphaseSystem.CreateProxies(fixture, worldPos, worldRot, false); + } + + // Supposed to be wrapped in density but eh + if (updates) + { + FixtureUpdate(manager, body); + body.ResetMassData(); + manager.Dirty(); + } + // TODO: Set newcontacts to true. + } + + /// + /// Creates a from this shape and adds it to the specified + /// + public Fixture CreateFixture(PhysicsComponent body, IPhysShape shape) + { + var fixture = new Fixture(body, shape); + CreateFixture(body, fixture); + return fixture; + } + + /// + /// Creates a from this shape and adds it to the specified with mass. + /// + public void CreateFixture(PhysicsComponent body, IPhysShape shape, float mass) + { + // TODO: Make it take in density instead? + var fixture = new Fixture(body, shape) {Mass = mass}; + CreateFixture(body, fixture); + } + + /// + /// Attempts to get the with the specified ID for this body. + /// + public Fixture? GetFixtureOrNull(PhysicsComponent body, string id, FixturesComponent? manager = null) + { + if (!Resolve(body.OwnerUid, ref manager)) + { + return null; + } + + return manager.Fixtures.TryGetValue(id, out var fixture) ? fixture : null; + } + + /// + /// Destroys the specified attached to the body. + /// + /// The specified body + /// The fixture ID + /// Whether to update mass etc. Set false if you're doing a bulk operation + public void DestroyFixture(PhysicsComponent body, string id, bool updates = true) + { + var fixture = GetFixtureOrNull(body, id); + + if (fixture == null) return; + + DestroyFixture(body, fixture, updates); + } + + /// + /// Destroys the specified + /// + /// The specified fixture + /// Whether to update mass etc. Set false if you're doing a bulk operation + public void DestroyFixture(Fixture fixture, bool updates = true, FixturesComponent? manager = null) + { + DestroyFixture(fixture.Body, fixture, updates, manager); + } + + /// + /// Destroys the specified + /// + /// Whether to update mass etc. Set false if you're doing a bulk operation + public void DestroyFixture(PhysicsComponent body, Fixture fixture, bool updates = true, FixturesComponent? manager = null) + { + if (!Resolve(body.OwnerUid, ref manager)) + { + return; + } + + // TODO: Assert world locked + DebugTools.Assert(fixture.Body == body); + DebugTools.Assert(manager.FixtureCount > 0); + + if (!manager.Fixtures.Remove(fixture.ID)) + { + Logger.ErrorS("fixtures", $"Tried to remove fixture from {body.Owner} that was already removed."); + return; + } + + var edge = body.ContactEdges; + + while (edge != null) + { + var contact = edge.Contact!; + edge = edge.Next; + + var fixtureA = contact.FixtureA; + var fixtureB = contact.FixtureB; + + if (fixture == fixtureA || fixture == fixtureB) + { + body.PhysicsMap?.ContactManager.Destroy(contact); + } + } + + var broadphase = body.Broadphase; + + if (broadphase != null) + { + _broadphaseSystem.DestroyProxies(broadphase, fixture); + } + + if (updates) + { + FixtureUpdate(manager, body); + body.ResetMassData(); + manager.Dirty(); + } + } + + #endregion + + private void OnPhysicsShutdown(EntityUid uid, PhysicsComponent component, ComponentShutdown args) + { + if (EntityManager.GetComponent(uid).EntityLifeStage > EntityLifeStage.MapInitialized) return; + EntityManager.RemoveComponent(uid); + } + + private void OnPhysicsInit(ref PhysicsInitializedEvent ev) + { + EntityManager.EnsureComponent(ev.Uid); + } + + private void OnGetState(EntityUid uid, FixturesComponent component, ref ComponentGetState args) + { + args.State = new FixtureManagerComponentState + { + Fixtures = component.Fixtures.Values.ToList(), + }; + } + + private void OnHandleState(EntityUid uid, FixturesComponent component, ref ComponentHandleState args) + { + if (args.Current is not FixtureManagerComponentState state) return; + + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physics)) + { + DebugTools.Assert(false); + Logger.ErrorS("physics", $"Tried to apply fixture state for {uid} which has name {nameof(PhysicsComponent)}"); + return; + } + + var toAddFixtures = new List(); + var toRemoveFixtures = new List(); + var computeProperties = false; + + // Given a bunch of data isn't serialized need to sort of re-initialise it + var newFixtures = new List(state.Fixtures.Count); + foreach (var fixture in state.Fixtures) + { + var newFixture = new Fixture(); + fixture.CopyTo(newFixture); + newFixture.Body = physics; + newFixtures.Add(newFixture); + } + + // Add / update new fixtures + foreach (var fixture in newFixtures) + { + var found = false; + + foreach (var (_, existing) in component.Fixtures) + { + if (!fixture.ID.Equals(existing.ID)) continue; + + if (!fixture.Equals(existing)) + { + toAddFixtures.Add(fixture); + toRemoveFixtures.Add(existing); + } + + found = true; + break; + } + + if (!found) + { + toAddFixtures.Add(fixture); + } + } + + // Remove old fixtures + foreach (var (_, existing) in component.Fixtures) + { + var found = false; + + foreach (var fixture in newFixtures) + { + if (fixture.ID.Equals(existing.ID)) + { + found = true; + break; + } + } + + if (!found) + { + toRemoveFixtures.Add(existing); + } + } + + foreach (var fixture in toRemoveFixtures) + { + computeProperties = true; + DestroyFixture(physics, fixture); + } + + // TODO: We also still need event listeners for shapes (Probably need C# events) + // Or we could just make it so shapes can only be updated via fixturesystem which handles it + // automagically (friends or something?) + foreach (var fixture in toAddFixtures) + { + computeProperties = true; + CreateFixture(physics, fixture); + } + + if (computeProperties) + { + physics.ResetMassData(); + } + } + + private string GetFixtureName(FixturesComponent component, Fixture fixture) + { + if (!string.IsNullOrEmpty(fixture.ID)) return fixture.ID; + + var i = 0; + + while (true) + { + ++i; + var name = $"fixture_{i}"; + var found = component.Fixtures.ContainsKey(name); + + if (!found) + { + return name; + } + } + } + + /// + /// Updates all of the cached physics information on the body derived from fixtures. + /// + public void FixtureUpdate(FixturesComponent component, PhysicsComponent? body = null) + { + if (!Resolve(component.OwnerUid, ref body)) + { + return; + } + + var mask = 0; + var layer = 0; + var hard = false; + + foreach (var (_, fixture) in component.Fixtures) + { + mask |= fixture.CollisionMask; + layer |= fixture.CollisionLayer; + hard |= fixture.Hard; + } + + body.ResetMassData(); + + // Normally this method is called when fixtures need to be dirtied anyway so no point in returning early I think + body.CollisionMask = mask; + body.CollisionLayer = layer; + body.Hard = hard; + component.Dirty(); + } + + [Serializable, NetSerializable] + private sealed class FixtureManagerComponentState : ComponentState + { + public List Fixtures = default!; + } + } +} diff --git a/Robust.Shared/Physics/FixturesComponent.cs b/Robust.Shared/Physics/FixturesComponent.cs new file mode 100644 index 000000000..dc134df41 --- /dev/null +++ b/Robust.Shared/Physics/FixturesComponent.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Robust.Shared.Physics +{ + /// + /// Storage for physics fixtures + /// + /// + /// In its own component to decrease physics comp state size significantly. + /// + [RegisterComponent] + [NetworkedComponent] + [Friend(typeof(FixtureSystem))] + [ComponentProtoName("Fixtures")] + public sealed class FixturesComponent : Component, ISerializationHooks + { + // This is a snowflake component whose main job is making physics states smaller for massive bodies + // (e.g. grids) + // Content generally shouldn't care about its existence. + + public override string Name => "Fixtures"; + + [ViewVariables] + public int FixtureCount => Fixtures.Count; + + [ViewVariables] + public readonly Dictionary Fixtures = new(); + + [DataField("fixtures")] + [NeverPushInheritance] + internal List SerializedFixtures = new(); + + void ISerializationHooks.BeforeSerialization() + { + // Can't assert the count is 0 because it's possible it gets re-serialized before init! + if (SerializedFixtures.Count > 0) return; + + foreach (var (_, fixture) in Fixtures) + { + SerializedFixtures.Add(fixture); + } + } + } +} diff --git a/Robust.Shared/Physics/IPhysBody.cs b/Robust.Shared/Physics/IPhysBody.cs index e2a9aa401..1cef69dbd 100644 --- a/Robust.Shared/Physics/IPhysBody.cs +++ b/Robust.Shared/Physics/IPhysBody.cs @@ -54,8 +54,6 @@ namespace Robust.Shared.Physics /// void DestroyContacts(); - IReadOnlyList Fixtures { get; } - /// /// The type of the body, which determines how collisions effect this object. /// @@ -83,7 +81,7 @@ namespace Robust.Shared.Physics /// /// This is useful for triggers or such to detect collision without actually causing a blockage. /// - bool Hard { get; set; } + bool Hard { get; } /// /// Inverse mass of the entity in kilograms (1 / Mass). diff --git a/Robust.Shared/Physics/SharedBroadphaseSystem.cs b/Robust.Shared/Physics/SharedBroadphaseSystem.cs index 3a4039e36..42e4bc4d4 100644 --- a/Robust.Shared/Physics/SharedBroadphaseSystem.cs +++ b/Robust.Shared/Physics/SharedBroadphaseSystem.cs @@ -183,7 +183,7 @@ namespace Robust.Shared.Physics } } - private void UpdateBroadphaseCache(BroadphaseComponent broadphase) + internal void UpdateBroadphaseCache(BroadphaseComponent broadphase) { var uid = broadphase.OwnerUid; @@ -409,14 +409,15 @@ namespace Robust.Shared.Physics /// /// Remove all of our fixtures from the broadphase. /// - /// - private void DestroyProxies(PhysicsComponent body) + private void DestroyProxies(PhysicsComponent body, FixturesComponent? manager = null) { + if (!Resolve(body.OwnerUid, ref manager)) return; + var broadphase = body.Broadphase; if (broadphase == null) return; - foreach (var fixture in body._fixtures) + foreach (var (_, fixture) in manager.Fixtures) { DestroyProxies(broadphase, fixture); } @@ -440,9 +441,15 @@ namespace Robust.Shared.Physics CreateProxies(body, true); } - internal void RemoveBody(PhysicsComponent body) + internal void RemoveBody(PhysicsComponent body, FixturesComponent? manager = null) { - DestroyProxies(body); + // TODO: Would reaaalllyy like for this to not be false in future + if (!Resolve(body.OwnerUid, ref manager, false)) + { + return; + } + + DestroyProxies(body, manager); } private void OnGridMove(EntityUid uid, MapGridComponent component, ref MoveEvent args) @@ -558,91 +565,6 @@ namespace Robust.Shared.Physics _queuedRotates.Enqueue(args); } - public void CreateFixture(PhysicsComponent body, Fixture fixture) - { - fixture.ID = body.GetFixtureName(fixture); - body._fixtures.Add(fixture); - body.FixtureCount += 1; - fixture.Body = body; - - // TODO: Assert world locked - // Broadphase should be set in the future TM - // Should only happen for nullspace / initializing entities - if (body.Broadphase != null) - { - UpdateBroadphaseCache(body.Broadphase); - CreateProxies(fixture, body.Owner.Transform.WorldPosition, false); - } - - // Supposed to be wrapped in density but eh - body.ResetMassData(); - body.Dirty(); - // TODO: Set newcontacts to true. - } - - public Fixture CreateFixture(PhysicsComponent body, IPhysShape shape) - { - var fixture = new Fixture(body, shape); - CreateFixture(body, fixture); - return fixture; - } - - public void CreateFixture(PhysicsComponent body, IPhysShape shape, float mass) - { - // TODO: Make it take in density instead - var fixture = new Fixture(body, shape) {Mass = mass}; - CreateFixture(body, fixture); - } - - public void DestroyFixture(PhysicsComponent body, string id) - { - var fixture = body.GetFixture(id); - if (fixture == null) return; - DestroyFixture(body, fixture); - } - - public void DestroyFixture(Fixture fixture) - { - DestroyFixture(fixture.Body, fixture); - } - - public void DestroyFixture(PhysicsComponent body, Fixture fixture) - { - // TODO: Assert world locked - DebugTools.Assert(fixture.Body == body); - DebugTools.Assert(body.FixtureCount > 0); - - if (!body._fixtures.Remove(fixture)) - return; - - var edge = body.ContactEdges; - - while (edge != null) - { - var contact = edge.Contact!; - edge = edge.Next; - - var fixtureA = contact.FixtureA; - var fixtureB = contact.FixtureB; - - if (fixture == fixtureA || fixture == fixtureB) - { - body.PhysicsMap?.ContactManager.Destroy(contact); - } - } - - var broadphase = GetBroadphase(fixture.Body); - - if (broadphase != null) - { - DestroyProxies(broadphase, fixture); - } - - body.FixtureCount -= 1; - body.ResetMassData(); - body.Dirty(); - } - private void SynchronizeFixtures(PhysicsComponent body, Vector2 worldPos, float worldRot) { // Logger.DebugS("physics", $"Synchronizing fixtures for {body.Owner}"); @@ -733,6 +655,7 @@ namespace Robust.Shared.Physics if (body.Owner.Transform.MapID == MapId.Nullspace) return; var worldPos = body.Owner.Transform.WorldPosition; + var worldRot = body.Owner.Transform.WorldRotation; // Outside of PVS (TODO Remove when PVS is better) if (float.IsNaN(worldPos.X) || float.IsNaN(worldPos.Y)) @@ -756,7 +679,7 @@ namespace Robust.Shared.Physics foreach (var fixture in body.Fixtures) { - CreateProxies(fixture, worldPos, useCache); + CreateProxies(fixture, worldPos, worldRot, useCache); } // Ensure cache remains up to date if the broadphase is moving. @@ -773,7 +696,7 @@ namespace Robust.Shared.Physics /// /// Create the proxies for this fixture on the body's broadphase. /// - private void CreateProxies(Fixture fixture, Vector2 worldPos, bool useCache) + internal void CreateProxies(Fixture fixture, Vector2 worldPos, Angle worldRot, bool useCache) { // Ideally we would always just defer this until Update / FrameUpdate but that will have to wait for a future // PR for my own sanity. @@ -800,7 +723,7 @@ namespace Robust.Shared.Physics Matrix3 broadphaseInvMatrix; (Vector2 Position, float Rotation) broadphaseTransform; - var xform = broadphase.Owner.Transform; + var xform = EntityManager.GetComponent(broadphase.OwnerUid); if (useCache) { @@ -817,7 +740,6 @@ namespace Robust.Shared.Physics broadphaseTransform = (wp, (float) wr.Theta); } - var worldRot = fixture.Body.Owner.Transform.WorldRotation; var localPos = broadphaseInvMatrix.Transform(worldPos); var transform = new Transform(localPos, worldRot - broadphaseTransform.Rotation); @@ -841,7 +763,7 @@ namespace Robust.Shared.Physics /// /// Destroy the proxies for this fixture on the broadphase. /// - private void DestroyProxies(BroadphaseComponent broadphase, Fixture fixture) + internal void DestroyProxies(BroadphaseComponent broadphase, Fixture fixture) { if (broadphase == null) { @@ -878,21 +800,6 @@ namespace Robust.Shared.Physics physicsComponent.Awake = true; } - private void OnMapCreated(object? sender, MapEventArgs e) - { - if (e.Map == MapId.Nullspace) return; - - var mapEnt = _mapManager.GetMapEntity(e.Map); - mapEnt.EnsureComponent(); - _moveBuffer[e.Map] = new Dictionary(64); - } - - private void OnMapDestroyed(object? sender, MapEventArgs e) - { - _moveBuffer.Remove(e.Map); - _movedGrids.Remove(e.Map); - } - public override void Shutdown() { base.Shutdown(); @@ -902,10 +809,25 @@ namespace Robust.Shared.Physics _mapManager.MapDestroyed -= OnMapDestroyed; } + #region Broadphase management + + private void OnMapCreated(object? sender, MapEventArgs e) + { + if (e.Map == MapId.Nullspace) return; + + EntityManager.EnsureComponent(_mapManager.GetMapEntityId(e.Map)); + _moveBuffer[e.Map] = new Dictionary(64); + } + + private void OnMapDestroyed(object? sender, MapEventArgs e) + { + _moveBuffer.Remove(e.Map); + _movedGrids.Remove(e.Map); + } + private void HandleGridInit(GridInitializeEvent ev) { - var grid = EntityManager.GetEntity(ev.EntityUid); - grid.EnsureComponent(); + EntityManager.EnsureComponent(ev.EntityUid); } private void HandleBroadphaseInit(EntityUid uid, BroadphaseComponent component, ComponentInit args) @@ -914,6 +836,8 @@ namespace Robust.Shared.Physics component.Tree = new DynamicTreeBroadPhase(capacity); } + #endregion + internal BroadphaseComponent? GetBroadphase(PhysicsComponent body) { return GetBroadphase(body.Owner); @@ -984,11 +908,6 @@ namespace Robust.Shared.Physics } } - internal IEnumerable GetBroadphases(MapId mapId, Vector2 worldPos) - { - return GetBroadphases(mapId, new Box2(worldPos, worldPos)); - } - private sealed class InvalidBroadphaseException : Exception { public InvalidBroadphaseException() {} diff --git a/Robust.Shared/SharedIoC.cs b/Robust.Shared/SharedIoC.cs index 14f46c4ee..e4c90f13a 100644 --- a/Robust.Shared/SharedIoC.cs +++ b/Robust.Shared/SharedIoC.cs @@ -48,7 +48,6 @@ namespace Robust.Shared IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); } } } diff --git a/Robust.UnitTesting/RobustUnitTest.cs b/Robust.UnitTesting/RobustUnitTest.cs index 4a8dd9eee..9be2a4b77 100644 --- a/Robust.UnitTesting/RobustUnitTest.cs +++ b/Robust.UnitTesting/RobustUnitTest.cs @@ -113,6 +113,16 @@ namespace Robust.UnitTesting compFactory.RegisterClass(); } + if (!compFactory.AllRegisteredTypes.Contains(typeof(BroadphaseComponent))) + { + compFactory.RegisterClass(); + } + + if (!compFactory.AllRegisteredTypes.Contains(typeof(FixturesComponent))) + { + compFactory.RegisterClass(); + } + if(entMan.EventBus == null) { entMan.Startup(); diff --git a/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs b/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs index 11e558453..70dcff521 100644 --- a/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs +++ b/Robust.UnitTesting/Server/Maps/MapLoaderTest.cs @@ -66,9 +66,12 @@ entities: var broady = new BroadPhaseSystem(); var physics = new PhysicsSystem(); var gridFixtures = new GridFixtureSystem(); + var fixtures = new FixtureSystem(); + // MOCKS WHY mock.Setup(m => m.GetEntitySystem()).Returns(broady); mock.Setup(m => m.GetEntitySystem()).Returns(physics); mock.Setup(m => m.GetEntitySystem()).Returns(gridFixtures); + mock.Setup(m => m.GetEntitySystem()).Returns(fixtures); IoCManager.RegisterInstance(mock.Object, true); //IoCManager.RegisterInstance(mockFormat.Object, true); @@ -103,7 +106,11 @@ entities: var entMan = IoCManager.Resolve(); var mapId = map.CreateMap(); - map.GetMapEntity(mapId).EnsureComponent(); + // Yay test bullshit + var mapUid = map.GetMapEntityId(mapId); + entMan.EnsureComponent(mapUid); + entMan.EnsureComponent(mapUid); + var mapLoad = IoCManager.Resolve(); var grid = mapLoad.LoadBlueprint(mapId, "/TestMap.yml"); diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index 5050fa04e..2c4c79ff0 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -235,6 +235,7 @@ namespace Robust.UnitTesting.Server compFactory.RegisterClass(); compFactory.RegisterClass(); compFactory.RegisterClass(); + compFactory.RegisterClass(); _regDelegate?.Invoke(compFactory); @@ -250,6 +251,7 @@ namespace Robust.UnitTesting.Server entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); + entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); entitySystemMan.LoadExtraSystemType(); diff --git a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs index 43cb5b604..e8b713a4c 100644 --- a/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/Systems/AnchoredSystemTests.cs @@ -35,10 +35,6 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems { f.LoadString(Prototypes); }) - .RegisterDependencies(f => - { - f.Register(); - }) .InitializeInstance(); var mapManager = sim.Resolve(); diff --git a/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs b/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs index 8babdcf70..2a1b2ba16 100644 --- a/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/CollisionWake_Test.cs @@ -17,6 +17,7 @@ namespace Robust.UnitTesting.Shared.Physics - type: Transform - type: Physics bodyType: Dynamic + - type: Fixtures - type: CollisionWake "; diff --git a/Robust.UnitTesting/Shared/Physics/ShapeManager_Test.cs b/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs similarity index 89% rename from Robust.UnitTesting/Shared/Physics/ShapeManager_Test.cs rename to Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs index 5b8914f13..bc0134f97 100644 --- a/Robust.UnitTesting/Shared/Physics/ShapeManager_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/FixtureShape_Test.cs @@ -7,21 +7,21 @@ using Robust.Shared.Physics.Collision.Shapes; namespace Robust.UnitTesting.Shared.Physics { [TestFixture] - [TestOf(typeof(IShapeManager))] - public class ShapeManager_Test : RobustUnitTest + [TestOf(typeof(FixtureSystem))] + public class FixtureShape_Test : RobustUnitTest { - private IShapeManager _shapeManager = default!; + private FixtureSystem _shapeManager = default!; [OneTimeSetUp] public void Setup() { - _shapeManager = new ShapeManager(); + _shapeManager = new FixtureSystem(); } [Test] public void TestCirclePoint() { - var circle = new PhysShapeCircle() {Radius = 0.5f}; + var circle = new PhysShapeCircle {Radius = 0.5f}; var transform = new Transform(0f); var posA = Vector2.One; var posB = Vector2.Zero; diff --git a/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs b/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs index 6eebfbae9..cb0a0ec08 100644 --- a/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/MapVelocity_Test.cs @@ -18,6 +18,7 @@ namespace Robust.UnitTesting.Shared.Physics components: - type: Physics bodyType: Dynamic + - type: Fixtures "; [Test] diff --git a/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs b/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs index e7f64c267..8fb9fb08c 100644 --- a/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/PhysicsComponent_Test.cs @@ -30,8 +30,8 @@ namespace Robust.UnitTesting.Shared.Physics { await _server.WaitIdleAsync(); var entManager = _server.ResolveDependency(); - var broadphaseSystem = _server.ResolveDependency() - .GetEntitySystem(); + var fixtureSystem = _server.ResolveDependency() + .GetEntitySystem(); await _server.WaitAssertion(() => { @@ -39,7 +39,7 @@ namespace Robust.UnitTesting.Shared.Physics var box = boxEnt.AddComponent(); var poly = new PolygonShape(); poly.SetAsBox(0.5f, 0.5f); - var fixture = broadphaseSystem.CreateFixture(box, poly); + var fixture = fixtureSystem.CreateFixture(box, poly); fixture.Mass = 1f; box.FixedRotation = false; box.BodyType = BodyType.Dynamic; diff --git a/Robust.UnitTesting/Shared/Physics/Stack_Test.cs b/Robust.UnitTesting/Shared/Physics/Stack_Test.cs index 667a23b9a..47e968aed 100644 --- a/Robust.UnitTesting/Shared/Physics/Stack_Test.cs +++ b/Robust.UnitTesting/Shared/Physics/Stack_Test.cs @@ -49,11 +49,11 @@ namespace Robust.UnitTesting.Shared.Physics var entityManager = server.ResolveDependency(); var mapManager = server.ResolveDependency(); var entitySystemManager = server.ResolveDependency(); - var broadphaseSystem = entitySystemManager.GetEntitySystem(); + var fixtureSystem = entitySystemManager.GetEntitySystem(); MapId mapId; - var columnCount = 1; - var rowCount = 15; + const int columnCount = 1; + const int rowCount = 15; PhysicsComponent[] bodies = new PhysicsComponent[columnCount * rowCount]; Vector2 firstPos = Vector2.Zero; @@ -73,7 +73,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true }; - broadphaseSystem.CreateFixture(ground, horizontalFixture); + fixtureSystem.CreateFixture(ground, horizontalFixture); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); var verticalFixture = new Fixture(ground, vertical) @@ -83,7 +83,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true }; - broadphaseSystem.CreateFixture(ground, verticalFixture); + fixtureSystem.CreateFixture(ground, verticalFixture); var xs = new[] { @@ -116,7 +116,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true, }; - broadphaseSystem.CreateFixture(box, fixture); + fixtureSystem.CreateFixture(box, fixture); bodies[j * rowCount + i] = box; } @@ -164,8 +164,7 @@ namespace Robust.UnitTesting.Shared.Physics var entityManager = server.ResolveDependency(); var mapManager = server.ResolveDependency(); var entitySystemManager = server.ResolveDependency(); - var physicsSystem = entitySystemManager.GetEntitySystem(); - var broadphaseSystem = entitySystemManager.GetEntitySystem(); + var fixtureSystem = entitySystemManager.GetEntitySystem(); MapId mapId; var columnCount = 1; @@ -188,7 +187,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true }; - broadphaseSystem.CreateFixture(ground, horizontalFixture); + fixtureSystem.CreateFixture(ground, horizontalFixture); var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10)); var verticalFixture = new Fixture(ground, vertical) @@ -198,7 +197,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true }; - broadphaseSystem.CreateFixture(ground, verticalFixture); + fixtureSystem.CreateFixture(ground, verticalFixture); var xs = new[] { @@ -227,7 +226,7 @@ namespace Robust.UnitTesting.Shared.Physics Hard = true, }; - broadphaseSystem.CreateFixture(circle, fixture); + fixtureSystem.CreateFixture(circle, fixture); bodies[j * rowCount + i] = circle; }