using System; using System.Linq; using System.Reflection; using Sandbox.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame.Utilities; using VRageMath; using Xunit; using P=AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class FleetTrafficTests { static FleetStateTests.Rig Arrivals(int count) { var r=new FleetStateTests.Rig(count,false); foreach(var t in r.Miners){t.State=P.FlightState.Holding;t.Position+=Vector3D.Forward*100;} r.RegisterAll();r.Tick(30);return r; } static void FreshTicks(FleetStateTests.Rig r,int count) {for(int n=0;np.Kind=="DOCK_GRANT").Select(p=>P.DockFrame.FromIni(p.Body)).ToList(); Assert.Equal(count,grants.Count);Assert.Equal(count,grants.Select(g=>g.ConnectorId).Distinct().Count()); Assert.Equal(count,grants.Select(g=>g.Token).Distinct().Count());Assert.Equal(count,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); } [Fact] public void OverlappingPortCorridorsSerializeAndDoNotExpireAnUnknownShip() { var r=Arrivals(2);r.SetPort(1,Vector3D.Right*4,Vector3D.Forward);r.Miners[1].Position=new Vector3D(4,0,-130); FreshTicks(r,30);foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30); var first=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");r.Sent.Clear();r.Tick(1000); int other=first.Target==r.Miners[0].Id?1:0;r.Receive("DOCK_REQUEST",r.Miners[other]);r.Tick(20); Assert.DoesNotContain(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); } [Fact] public void ArrivalAndDepartureOnSeparateCorridorsRunTogether() { var r=new FleetStateTests.Rig(2,true);r.SetConnected(1,false);r.Miners[1].Position=new Vector3D(40,0,-100);r.Miners[1].State=P.FlightState.Holding; r.RegisterAll();r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-200: GPS:inward:0:0:-201:");r.Fleet.Command("start");FreshTicks(r,30); r.Miners[0].JobId=r.Fleet.GetJobs().Single().Id;r.Receive("DEPART_REQUEST",r.Miners[0]);r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,30); Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Single(r.Sent,p=>p.Kind=="DEPART_GRANT");Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); } [Fact] public void StoppedFaultAwayFromThePortReleasesItsLaneAndItsRemoteTask() { var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15); r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-300: GPS:inward:0:0:-301:");var job=r.Fleet.GetJobs().Single();r.Planner.SetOwner(job,r.Miners[0].Id); r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=new Vector3D(-50,0,-100);r.Receive("TELEMETRY",r.Miners[0]); r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,20); Assert.Equal("",job.Owner);Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); Assert.Equal(r.Miners[1].Id,P.Data.Text(r.Save(),"fleet","LaneOwner"));Assert.Contains(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id); } [Theory] [InlineData(true)] [InlineData(false)] public void FaultAtAContactOnlyBlocksTheAffectedCorridor(bool stale) { var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15); r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20;r.Receive("TELEMETRY",r.Miners[0]); if(stale)r.Program.Now+=10;else r.Tick(12); r.Receive("DOCK_REQUEST",r.Miners[1]);r.Tick(12); var second=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id); Assert.Equal(101,P.DockFrame.FromIni(second.Body).ConnectorId); Assert.Equal(stale?2:1,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); } [Fact] public void DockRetreatReleaseAcceptsAStoppedShipBeforeTheFarWaitingPoint() { var r=Arrivals(1);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);var grant=P.DockFrame.FromIni(r.Sent.First(p=>p.Kind=="DOCK_GRANT").Body); r.Miners[0].Position=Vector3D.Forward*35;r.Miners[0].State=P.FlightState.DockRetreat;r.Receive("TELEMETRY",r.Miners[0]); var body=new MyIni();body.Set("lease","Token",grant.Token);r.Fleet.Receive(new P.Packet {Kind="LANE_RELEASE",From=r.Miners[0].Id,Source=5000,Body=body}); Assert.Equal(0,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); } [Fact] public void MultipleUnknownLanesSurviveSaveRemovalAndReloadWithTokensRevoked() { var r=Arrivals(2);r.Checkpoint();foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30);Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount")); r.Program.Now+=10;r.Fleet.Command("clear-offline");var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage); Assert.Empty(after.Fleet.GetTelemetry());var saved=after.Save();Assert.Equal(2,P.Data.ReadLong(saved,"fleet","LaneCount")); Assert.Empty(P.Data.Text(saved,"fleet","LaneToken"));Assert.Empty(P.Data.Text(saved,"fleetLane1","LaneToken")); Assert.Equal(2,P.Data.ReadLong(saved,"fleet","ProtectedOwnerCount")); } static P.Packet Traffic(FleetStateTests.Rig r,int index) { typeof(P.FleetController).GetMethod("SendTraffic",BindingFlags.NonPublic|BindingFlags.Instance).Invoke(r.Fleet,new object[]{r.Fleet.GetTelemetry().Single(t=>t.Id==r.Miners[index].Id)}); return r.Sent.Last(p=>p.Kind=="TRAFFIC" && p.Target==r.Miners[index].Id); } [Fact] public void CrossingCruisePathsHaveDeterministicYieldPriority() { var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=b.State=P.FlightState.Transit;a.HasRoute=b.HasRoute=true; a.Position=Vector3D.Left*20;a.RouteTarget=Vector3D.Right*20;a.Velocity=Vector3D.Right*3; b.Position=Vector3D.Forward*20;b.RouteTarget=Vector3D.Backward*20;b.Velocity=Vector3D.Backward*3; a.ThrustMargin=b.ThrustMargin=2;r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b); Assert.False(P.Data.Flag(Traffic(r,0).Body,"traffic","Wait"));Assert.True(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait")); } [Fact] public void StationaryFaultOnTheCruiseLineCreatesACameraCheckedDetour() { var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true; a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;a.ThrustMargin=2;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault; r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b);var body=Traffic(r,0).Body; Assert.True(P.Data.Flag(body,"traffic","Avoid"));Assert.False(P.Data.Flag(body,"traffic","Wait")); var point=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);Assert.True(Vector3D.Distance(point,b.Position)>a.Radius+b.Radius+4); } [Fact] public void StaleFaultPositionCannotAuthorizeAnUnverifiedDetour() { var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true; a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault;r.Receive("TELEMETRY",b); r.Program.Now+=5;r.Receive("TELEMETRY",a);var body=Traffic(r,0).Body;Assert.True(P.Data.Flag(body,"traffic","Wait"));Assert.False(P.Data.Flag(body,"traffic","Avoid")); } [Fact] public void AStoppedFaultStillProtectsItsPointAfterOfflineRegistryRemoval() { var r=Arrivals(2);r.Checkpoint();r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20; r.Receive("TELEMETRY",r.Miners[0]);r.Tick(12);r.Program.Now+=10;r.Receive("TELEMETRY",r.Miners[1]);r.Fleet.Command("clear-offline"); var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage); Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ClearanceCount"));Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ProtectedOwnerCount")); } [Fact] public void FrontShipDoesNotYieldToItsFollower() { var r=Arrivals(2);var rear=r.Miners[0];var front=r.Miners[1];rear.HasRoute=front.HasRoute=true;rear.State=front.State=P.FlightState.Transit; rear.Position=Vector3D.Forward*80;front.Position=Vector3D.Forward*60;rear.RouteTarget=front.RouteTarget=Vector3D.Zero; rear.Velocity=front.Velocity=Vector3D.Backward*3;r.Receive("TELEMETRY",rear);r.Receive("TELEMETRY",front); Assert.False(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait")); } sealed class MovingShip { public readonly ReturnNavigationTests.Rig R=new ReturnNavigationTests.Rig(); public Vector3D Position,Velocity,Target; public bool Arrived; public MovingShip(Vector3D start,Vector3D target) { Position=start;Target=target;R.Orientation=MatrixD.CreateWorld(start,Vector3D.Normalize(target-start),Vector3D.Up); R.Controller.Method("GetPosition",a=>Position).Method("GetShipVelocities",a=>new MyShipVelocities(Velocity,Vector3D.Zero)); R.Camera.Method("GetPosition",a=>Position).Method("CanScan",a=>true); } public void Step(double now) { R.Program.Now=now;Arrived=R.Flight.Move(Target,Vector3D.Zero,Vector3D.Normalize(Target-Position),Vector3D.Up,5,.5,true,.05,true); Vector3D force=Vector3D.Zero;foreach(var thrust in R.Ship.Thrusters)force+=thrust.WorldMatrix.Backward*thrust.ThrustOverridePercentage*thrust.MaxEffectiveThrust; Velocity+=force/1000*.05;Position+=Velocity*.05; var desired=(MatrixD)typeof(P.FlightController).GetField("previousDesired",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(R.Flight); R.Orientation=MatrixD.CreateWorld(Position,desired.Forward,desired.Up); } } [Fact] public void CrossingShipsBothReachTheirGoalsWithPhysicalSeparationInTheControlModel() { var r=Arrivals(2);var ships=new[]{new MovingShip(Vector3D.Left*40,Vector3D.Right*40),new MovingShip(Vector3D.Forward*40,Vector3D.Backward*40)}; double separation=double.MaxValue;bool yielded=false; for(int n=0;n<2000 && ships.Any(s=>!s.Arrived);n++) { r.Program.Now+=.05; if(n%10==0) { for(int i=0;i<2;i++) { var t=r.Miners[i];var s=ships[i];t.State=P.FlightState.Transit;t.Position=s.Position;t.Velocity=s.Velocity;t.Radius=2;t.ThrustMargin=100; t.HasRoute=!s.Arrived;t.RouteTarget=s.R.Flight.HasRoute?s.R.Flight.RouteTarget:s.Target;r.Receive("TELEMETRY",t); } for(int i=0;i<2;i++)if(!ships[i].Arrived) { var body=Traffic(r,i).Body;var f=ships[i].R.Flight;f.TrafficWait=P.Data.Flag(body,"traffic","Wait");f.TrafficAvoid=P.Data.Flag(body,"traffic","Avoid"); f.TrafficPoint=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);f.TrafficAt=r.Program.Now;yielded|=f.TrafficWait; } } foreach(var s in ships)s.Step(r.Program.Now); separation=Math.Min(separation,Vector3D.Distance(ships[0].Position,ships[1].Position)); } Assert.True(yielded);Assert.True(ships.All(s=>s.Arrived));Assert.True(separation>=5,"Minimum separation: "+separation); } [Fact] public void FinalDockingDoesNotUseAnOldTrafficWaitOrDetour() { var r=new ReturnNavigationTests.Rig();r.Flight.TrafficWait=r.Flight.TrafficAvoid=true;r.Flight.TrafficPoint=Vector3D.Right*100;r.Flight.TrafficAt=0; r.Flight.Move(Vector3D.Forward*3,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,.2,.03,false,.05,false,false); Assert.True(r.Flight.CommandSpeed>0);Assert.Equal(0,r.Rays);Assert.Equal(Vector3D.Forward*3,r.Flight.RouteTarget); } } }