Docking config changes (#16945)

* Docking config changes

- Should be more flexible with tight bounds.
- Arrivals should always go for the 4-way dock.
- Don't think it fixes Omega.

* weh

* Basic test
This commit is contained in:
metalgearsloth
2023-06-01 00:09:14 +10:00
committed by GitHub
parent 30a36b2fd5
commit 56371c3acb
5 changed files with 136 additions and 30 deletions

View File

@@ -559,18 +559,22 @@ we are just going to end this here to save a lot of time. This is the exception
public static async Task<TestMapData> CreateTestMap(PairTracker pairTracker) public static async Task<TestMapData> CreateTestMap(PairTracker pairTracker)
{ {
var server = pairTracker.Pair.Server; var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var settings = pairTracker.Pair.Settings; var settings = pairTracker.Pair.Settings;
var mapManager = server.ResolveDependency<IMapManager>();
var tileDefinitionManager = server.ResolveDependency<ITileDefinitionManager>();
if (settings.NoServer) throw new Exception("Cannot setup test map without server"); if (settings.NoServer) throw new Exception("Cannot setup test map without server");
var mapData = new TestMapData(); var mapData = new TestMapData();
await server.WaitPost(() => await server.WaitPost(() =>
{ {
var mapManager = IoCManager.Resolve<IMapManager>();
mapData.MapId = mapManager.CreateMap(); mapData.MapId = mapManager.CreateMap();
mapData.MapUid = mapManager.GetMapEntityId(mapData.MapId); mapData.MapUid = mapManager.GetMapEntityId(mapData.MapId);
mapData.MapGrid = mapManager.CreateGrid(mapData.MapId); mapData.MapGrid = mapManager.CreateGrid(mapData.MapId);
mapData.GridUid = mapData.MapGrid.Owner; mapData.GridUid = mapData.MapGrid.Owner;
mapData.GridCoords = new EntityCoordinates(mapData.GridUid, 0, 0); mapData.GridCoords = new EntityCoordinates(mapData.GridUid, 0, 0);
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var plating = tileDefinitionManager["Plating"]; var plating = tileDefinitionManager["Plating"];
var platingTile = new Tile(plating.TileId); var platingTile = new Tile(plating.TileId);
mapData.MapGrid.SetTile(mapData.GridCoords, platingTile); mapData.MapGrid.SetTile(mapData.GridCoords, platingTile);

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Shuttles.Systems;
using Content.Tests;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests.Shuttle;
public sealed class DockTest : ContentUnitTest
{
static IEnumerable<object[]> TestSource()
{
// I-shape for grid1, T-shape for grid2
yield return new object[] { new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Angle.Zero, Angle.Zero, true };
yield return new object[] { new Vector2(0.5f, 1.5f), new Vector2(0.5f, 1.5f), Angle.Zero, Angle.Zero, false };
}
[Test]
[TestCaseSource(nameof(TestSource))]
public async Task TestDockingConfig(Vector2 dock1Pos, Vector2 dock2Pos, Angle dock1Angle, Angle dock2Angle, bool result)
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings() { NoClient = true });
var server = pair.Pair.Server;
var map = await PoolManager.CreateTestMap(pair);
var entManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var dockingSystem = entManager.System<DockingSystem>();
var mapId = map.MapId;
await server.WaitAssertion(() =>
{
entManager.DeleteEntity(map.GridUid);
var grid1 = mapManager.CreateGrid(mapId);
var grid2 = mapManager.CreateGrid(mapId);
var grid2Offset = new Vector2(50f, 50f);
entManager.GetComponent<TransformComponent>(grid2.Owner).LocalPosition = grid2Offset;
// Tetris tests
// Grid1 is a vertical I
// Grid2 is a T
var tiles1 = new List<(Vector2i Index, Tile Tile)>()
{
new(new Vector2i(0, 0), new Tile(1)),
new(new Vector2i(0, 1), new Tile(1)),
new(new Vector2i(0, 2), new Tile(1)),
};
grid1.SetTiles(tiles1);
var dock1 = entManager.SpawnEntity("AirlockShuttle", new EntityCoordinates(grid1.Owner, dock1Pos));
var dock1Xform = entManager.GetComponent<TransformComponent>(dock1);
dock1Xform.LocalRotation = dock1Angle;
var tiles2 = new List<(Vector2i Index, Tile Tile)>()
{
new(new Vector2i(0, 0), new Tile(1)),
new(new Vector2i(0, 1), new Tile(1)),
new(new Vector2i(0, 2), new Tile(1)),
new(new Vector2i(-1, 2), new Tile(1)),
new(new Vector2i(1, 2), new Tile(1)),
};
grid2.SetTiles(tiles2);
var dock2 = entManager.SpawnEntity("AirlockShuttle", new EntityCoordinates(grid2.Owner, dock2Pos));
var dock2Xform = entManager.GetComponent<TransformComponent>(dock2);
dock2Xform.LocalRotation = dock2Angle;
var config = dockingSystem.GetDockingConfig(grid1.Owner, grid2.Owner);
Assert.That(result, Is.EqualTo(config != null));
});
await pair.CleanReturnAsync();
}
}

View File

@@ -13,11 +13,6 @@ public sealed class DockingConfig
/// </summary> /// </summary>
public List<(EntityUid DockAUid, EntityUid DockBUid, DockingComponent DockA, DockingComponent DockB)> Docks = new(); public List<(EntityUid DockAUid, EntityUid DockBUid, DockingComponent DockA, DockingComponent DockB)> Docks = new();
/// <summary>
/// Area relative to the target grid the emergency shuttle will spawn in on.
/// </summary>
public Box2 Area;
/// <summary> /// <summary>
/// Target grid for docking. /// Target grid for docking.
/// </summary> /// </summary>

View File

@@ -4,6 +4,7 @@ using Content.Server.Shuttles.Components;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
namespace Content.Server.Shuttles.Systems; namespace Content.Server.Shuttles.Systems;
@@ -38,16 +39,17 @@ public sealed partial class DockingSystem
TransformComponent shuttleDockXform, TransformComponent shuttleDockXform,
DockingComponent gridDock, DockingComponent gridDock,
TransformComponent gridDockXform, TransformComponent gridDockXform,
Angle targetGridRotation,
Box2 shuttleAABB, Box2 shuttleAABB,
Angle targetGridRotation,
FixturesComponent shuttleFixtures,
MapGridComponent grid, MapGridComponent grid,
[NotNullWhen(true)] out Box2? shuttleDockedAABB,
out Matrix3 matty, out Matrix3 matty,
[NotNullWhen(true)] out Box2? shuttleDockedAABB,
out Angle gridRotation) out Angle gridRotation)
{ {
shuttleDockedAABB = null;
gridRotation = Angle.Zero; gridRotation = Angle.Zero;
matty = Matrix3.Identity; matty = Matrix3.Identity;
shuttleDockedAABB = null;
if (shuttleDock.Docked || if (shuttleDock.Docked ||
gridDock.Docked || gridDock.Docked ||
@@ -64,18 +66,17 @@ public sealed partial class DockingSystem
// Need to invert the grid's angle. // Need to invert the grid's angle.
var shuttleDockAngle = shuttleDockXform.LocalRotation; var shuttleDockAngle = shuttleDockXform.LocalRotation;
var gridDockAngle = gridDockXform.LocalRotation.Opposite(); var gridDockAngle = gridDockXform.LocalRotation.Opposite();
var offsetAngle = gridDockAngle - shuttleDockAngle;
var stationDockMatrix = Matrix3.CreateInverseTransform(stationDockPos, shuttleDockAngle); var stationDockMatrix = Matrix3.CreateInverseTransform(stationDockPos, shuttleDockAngle);
var gridXformMatrix = Matrix3.CreateTransform(gridDockXform.LocalPosition, gridDockAngle); var gridXformMatrix = Matrix3.CreateTransform(gridDockXform.LocalPosition, gridDockAngle);
Matrix3.Multiply(in stationDockMatrix, in gridXformMatrix, out matty); Matrix3.Multiply(in stationDockMatrix, in gridXformMatrix, out matty);
shuttleDockedAABB = matty.TransformBox(shuttleAABB);
// Rounding moment
shuttleDockedAABB = shuttleDockedAABB.Value.Enlarged(-0.01f);
if (!ValidSpawn(grid, shuttleDockedAABB.Value)) if (!ValidSpawn(grid, matty, offsetAngle, shuttleFixtures))
return false; return false;
gridRotation = (targetGridRotation + gridDockAngle - shuttleDockAngle).Reduced(); shuttleDockedAABB = matty.TransformBox(shuttleAABB);
gridRotation = (targetGridRotation + offsetAngle).Reduced();
return true; return true;
} }
@@ -129,16 +130,19 @@ public sealed partial class DockingSystem
var targetGridGrid = Comp<MapGridComponent>(targetGrid); var targetGridGrid = Comp<MapGridComponent>(targetGrid);
var targetGridXform = xformQuery.GetComponent(targetGrid); var targetGridXform = xformQuery.GetComponent(targetGrid);
var targetGridAngle = _transform.GetWorldRotation(targetGridXform).Reduced(); var targetGridAngle = _transform.GetWorldRotation(targetGridXform).Reduced();
var shuttleFixturesComp = Comp<FixturesComponent>(shuttleUid);
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB; var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
var foundDocks = new HashSet<EntityUid>();
var validDockConfigs = new List<DockingConfig>(); var validDockConfigs = new List<DockingConfig>();
if (shuttleDocks.Count > 0) if (shuttleDocks.Count > 0)
{ {
// We'll try all combinations of shuttle docks and see which one is most suitable // We'll try all combinations of shuttle docks and see which one is most suitable
foreach (var (dockUid, shuttleDock) in shuttleDocks) foreach (var (dockUid, shuttleDock) in shuttleDocks)
{ {
if (foundDocks.Contains(dockUid))
continue;
var shuttleDockXform = xformQuery.GetComponent(dockUid); var shuttleDockXform = xformQuery.GetComponent(dockUid);
foreach (var (gridDockUid, gridDock) in gridDocks) foreach (var (gridDockUid, gridDock) in gridDocks)
@@ -148,11 +152,12 @@ public sealed partial class DockingSystem
if (!CanDock( if (!CanDock(
shuttleDock, shuttleDockXform, shuttleDock, shuttleDockXform,
gridDock, gridXform, gridDock, gridXform,
targetGridAngle,
shuttleAABB, shuttleAABB,
targetGridAngle,
shuttleFixturesComp,
targetGridGrid, targetGridGrid,
out var dockedAABB,
out var matty, out var matty,
out var dockedAABB,
out var targetAngle)) out var targetAngle))
{ {
continue; continue;
@@ -162,6 +167,7 @@ public sealed partial class DockingSystem
var spawnPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero)); var spawnPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero));
spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, spawnPosition.ToMapPos(EntityManager, _transform)); spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, spawnPosition.ToMapPos(EntityManager, _transform));
// TODO: use tight bounds
var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetAngle, spawnPosition.Position); var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetAngle, spawnPosition.Position);
// Check if there's no intersecting grids (AKA oh god it's docking at cargo). // Check if there's no intersecting grids (AKA oh god it's docking at cargo).
@@ -182,7 +188,7 @@ public sealed partial class DockingSystem
foreach (var (otherUid, other) in shuttleDocks) foreach (var (otherUid, other) in shuttleDocks)
{ {
if (other == shuttleDock) if (other == shuttleDock || foundDocks.Contains(otherUid))
continue; continue;
foreach (var (otherGridUid, otherGrid) in gridDocks) foreach (var (otherGridUid, otherGrid) in gridDocks)
@@ -195,13 +201,14 @@ public sealed partial class DockingSystem
xformQuery.GetComponent(otherUid), xformQuery.GetComponent(otherUid),
otherGrid, otherGrid,
xformQuery.GetComponent(otherGridUid), xformQuery.GetComponent(otherGridUid),
shuttleAABB,
targetGridAngle, targetGridAngle,
shuttleAABB, targetGridGrid, shuttleFixturesComp, targetGridGrid,
out var otherDockedAABB,
out _, out _,
out var otherdockedAABB,
out var otherTargetAngle) || out var otherTargetAngle) ||
!otherDockedAABB.Equals(dockedAABB) || !targetAngle.Equals(otherTargetAngle) ||
!targetAngle.Equals(otherTargetAngle)) !dockedAABB.Equals(otherdockedAABB))
{ {
continue; continue;
} }
@@ -213,21 +220,25 @@ public sealed partial class DockingSystem
validDockConfigs.Add(new DockingConfig() validDockConfigs.Add(new DockingConfig()
{ {
Docks = dockedPorts, Docks = dockedPorts,
Area = dockedAABB.Value,
Coordinates = spawnPosition, Coordinates = spawnPosition,
Angle = targetAngle, Angle = targetAngle,
}); });
foreach (var dock in dockedPorts)
{
foundDocks.Add(dock.DockAUid);
}
} }
} }
} }
if (validDockConfigs.Count <= 0) if (validDockConfigs.Count <= 0)
return null; return null;
// Prioritise by priority docks, then by maximum connected ports, then by most similar angle. // Prioritise by priority docks, then by maximum connected ports, then by most similar angle.
validDockConfigs = validDockConfigs validDockConfigs = validDockConfigs
.OrderByDescending(x => x.Docks.Any(docks => .OrderByDescending(x => x.Docks.Any(docks =>
TryComp<PriorityDockComponent>(docks.DockB.Owner, out var priority) && TryComp<PriorityDockComponent>(docks.DockBUid, out var priority) &&
priority.Tag?.Equals(priorityTag) == true)) priority.Tag?.Equals(priorityTag) == true))
.ThenByDescending(x => x.Docks.Count) .ThenByDescending(x => x.Docks.Count)
.ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList(); .ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList();
@@ -242,9 +253,22 @@ public sealed partial class DockingSystem
/// <summary> /// <summary>
/// Checks whether the emergency shuttle can warp to the specified position. /// Checks whether the emergency shuttle can warp to the specified position.
/// </summary> /// </summary>
private bool ValidSpawn(MapGridComponent grid, Box2 area) private bool ValidSpawn(MapGridComponent grid, Matrix3 matty, Angle angle, FixturesComponent shuttleFixturesComp)
{ {
return !grid.GetLocalTilesIntersecting(area).Any(); var transform = new Transform(matty.Transform(Vector2.Zero), angle);
// Because some docking bounds are tight af need to check each chunk individually
foreach (var fix in shuttleFixturesComp.Fixtures.Values)
{
var polyShape = (PolygonShape) fix.Shape;
var aabb = polyShape.ComputeAABB(transform, 0);
aabb = aabb.Enlarged(-0.01f);
if (grid.GetLocalTilesIntersecting(aabb).Any())
return false;
}
return true;
} }
public List<(EntityUid Uid, DockingComponent Component)> GetDocks(EntityUid uid) public List<(EntityUid Uid, DockingComponent Component)> GetDocks(EntityUid uid)

View File

@@ -20,6 +20,7 @@ using Robust.Server.Maps;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -161,7 +162,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
RaiseNetworkEvent(new EmergencyShuttlePositionMessage() RaiseNetworkEvent(new EmergencyShuttlePositionMessage()
{ {
StationUid = targetGrid, StationUid = targetGrid,
Position = config.Area, Position = Comp<MapGridComponent>(stationShuttle.EmergencyShuttle.Value).LocalAABB.Translated(config.Coordinates.Position)
}); });
} }