XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
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 ContinuousExplorationTests
    {
        static readonly BindingFlags Private=BindingFlags.Instance|BindingFlags.NonPublic;
        static T Read<T>(object o,string name) {return (T)o.GetType().GetField(name,Private).GetValue(o);}
        static void Write(object o,string name,object value) {o.GetType().GetField(name,Private).SetValue(o,value);}
        static void Call(object o,string name,params object[] args) {o.GetType().GetMethod(name,Private).Invoke(o,args);}
        static MinerStateTests.MinerRig CompletedRay()
        {
            var r=new MinerStateTests.MinerRig();r.Task();Read<P.Job>(r.Miner,"job").Kind=P.JobKind.Survey;
            Write(r.Miner,"home",new P.DockFrame {ReceivedAt=r.Program.Now});r.State(P.FlightState.Survey);Call(r.Miner,"Survey",.1);
            Assert.Equal(P.FlightState.Survey,r.StateNow);Assert.Null(Read<P.Job>(r.Miner,"job"));return r;
        }
        static P.Packet NextJob(MinerStateTests.MinerRig r,string after="hole")
        {
            var job=new P.Job {Id="next-hole",RegionId="area",Kind=P.JobKind.Survey,Entry=Vector3D.Forward*30,Depth=200,Owner=r.Program.Config.Id};
            var body=job.ToIni();body.Set("next","After",after);
            return new P.Packet {Kind="JOB",From=r.Program.Config.BaseId,Body=body};
        }
        [Fact]
        public void OneRayCompletesAndNextRayStartsWithoutADockingRoundTrip()
        {
            var r=CompletedRay();Assert.Equal("hole",r.Miner.GetTelemetry()[0].NextAfter);
            r.Resources.Cargo=.6;r.Resources.Battery=.8;r.Resources.NeedsCharge=true;
            r.Miner.Receive(NextJob(r));Assert.Equal(P.FlightState.Transit,r.StateNow);
            Assert.Equal("next-hole",r.Miner.GetJobs().Single().Id);Assert.Empty(r.Miner.GetTelemetry()[0].NextAfter);
            Assert.DoesNotContain(r.Sent,p=>p.Kind=="DOCK_REQUEST");
        }
        [Theory]
        [InlineData("pause")]
        [InlineData("return")]
        [InlineData("release")]
        public void LateContinuationNeverOverridesOperatorControl(string command)
        {
            var r=CompletedRay();r.Miner.Command(command);P.FlightState stopped=r.StateNow;
            r.Miner.Receive(NextJob(r));Assert.Equal(stopped,r.StateNow);Assert.False(Read<bool>(r.Miner,"running"));
        }
        [Fact]
        public void UnrelatedLateJobCannotReplaceAnAirborneContinuation()
        {
            var r=CompletedRay();r.Miner.Receive(NextJob(r,"old-hole"));Assert.Empty(r.Miner.GetJobs());Assert.Equal(P.FlightState.Survey,r.StateNow);
        }
        [Theory]
        [InlineData(.95,10)]
        [InlineData(.1,.01)]
        public void CargoOrInsufficientNextLegBudgetReturnsWithTheAssignedTaskPreserved(double cargo,double energy)
        {
            var r=CompletedRay();r.Resources.Cargo=cargo;r.Resources.StoredMWh=energy;
            r.Miner.Receive(NextJob(r));Assert.Equal(P.FlightState.Returning,r.StateNow);Assert.Equal("next-hole",r.Miner.GetJobs().Single().Id);
        }
        [Fact]
        public void CompletingAProbeContinuesOnlyAfterLeavingTheBore()
        {
            var r=new MinerStateTests.MinerRig();r.Task(40);r.State(P.FlightState.Retreat);Write(r.Miner,"completion","Complete");
            r.Position=Vector3D.Forward*10;Call(r.Miner,"Retreat",.1);Assert.Equal(P.FlightState.Retreat,r.StateNow);Assert.DoesNotContain(r.Sent,p=>p.Kind=="RESULT");
            r.Position=Vector3D.Backward*22;Call(r.Miner,"Retreat",.1);
            Assert.Equal(P.FlightState.Survey,r.StateNow);Assert.False(r.Drill.Value.Enabled);Assert.Equal("hole",r.Miner.GetTelemetry()[0].NextAfter);
        }
        [Fact]
        public void NavigationVoxelHitBecomesASurveyResultBeforeReachingTheObstructedObservation()
        {
            var r=new MinerStateTests.MinerRig();r.Task();var job=Read<P.Job>(r.Miner,"job");job.Kind=P.JobKind.Survey;job.Entry=Vector3D.Forward*50;
            r.Camera.Set("WorldMatrix",MatrixD.Identity).Set("RaycastConeLimit",45f).Set("RaycastDistanceLimit",-1d);
            r.CameraHit=MinerStateTests.MinerRig.Hit(MyDetectedEntityType.Asteroid);r.Position=Vector3D.Backward*20;r.State(P.FlightState.Transit);
            Call(r.Miner,"Transit",.1);
            var report=P.JobReport.FromIni(Assert.Single(r.Sent,p=>p.Kind=="RESULT").Body);
            Assert.Equal("SurveyHit",report.Outcome);Assert.Equal(42,report.EntityId);Assert.Equal(Vector3D.Zero,report.Position);
            Assert.Equal(P.FlightState.Survey,r.StateNow);Assert.Equal(Vector3D.Backward*20,r.Position);
        }
        [Fact]
        public void WaitingResultSurvivesReloadAndRequiresExplicitResume()
        {
            var before=CompletedRay();var save=new MyIni();before.Miner.Save(save);
            var after=new MinerStateTests.MinerRig();after.Miner.Load(save);Assert.Equal(P.FlightState.Paused,after.StateNow);
            Assert.Empty(after.Miner.GetTelemetry()[0].NextAfter);after.Miner.Command("resume");Assert.Equal(P.FlightState.Survey,after.StateNow);
            after.Program.Now=3;Call(after.Miner,"Survey",.1);
            Assert.Equal("hole",P.JobReport.FromIni(Assert.Single(after.Sent,p=>p.Kind=="RESULT").Body).JobId);
        }
        [Fact]
        public void OnlyMatchingRegionCompletionReturnsAnAwaitingMiner()
        {
            var r=CompletedRay();var body=new MyIni();body.Set("next","Done",true);body.Set("next","After","obsolete");
            var packet=new P.Packet {Kind="NEXT",From=r.Program.Config.BaseId,Body=body};r.Miner.Receive(packet);Assert.Equal(P.FlightState.Survey,r.StateNow);
            body.Set("next","After","hole");r.Miner.Receive(packet);Assert.Equal(P.FlightState.Returning,r.StateNow);
        }
        static FleetStateTests.Rig FleetAfterRay(bool hit=false)
        {
            var r=new FleetStateTests.Rig(1,true);r.RegisterAll();r.Fleet.Command("survey 300");while(r.Planner.Generating)r.Tick(1);
            var j=r.Planner.Jobs.First();r.Planner.SetOwner(j,r.Miners[0].Id);r.SetConnected(0,false);
            var t=r.Miners[0];t.Position=j.Entry;t.State=P.FlightState.Survey;t.JobId=j.Id;r.Receive("TELEMETRY",t);
            var result=new P.JobReport {JobId=j.Id,Outcome=hit?"SurveyHit":"SurveyEmpty",Position=j.Entry+j.Direction*10,Direction=j.Direction,Up=j.Up,EntityId=hit?42:0};
            r.Fleet.Receive(new P.Packet {Kind="RESULT",From=t.Id,Source=5000,Body=result.ToIni()});
            t.JobId="";t.NextAfter=j.Id;r.Receive("TELEMETRY",t);r.Sent.Clear();return r;
        }
        static void Dispatch(FleetStateTests.Rig r)
        {
            r.Fleet.Command("start");for(int n=0;n<400 && !r.Sent.Any(p=>p.Kind=="JOB" || p.Kind=="NEXT");n++) {if(n%5==0)r.Receive("TELEMETRY",r.Miners[0]);r.Tick(1);}
        }
        [Theory]
        [InlineData(false)]
        [InlineData(true)]
        public void FleetReassignsInSpaceAndPrefersNewProbesToFurtherRays(bool hit)
        {
            var r=FleetAfterRay(hit);Dispatch(r);var packet=Assert.Single(r.Sent,p=>p.Kind=="JOB");var j=P.Job.FromIni(packet.Body);
            Assert.Equal(hit?P.JobKind.Probe:P.JobKind.Survey,j.Kind);Assert.Equal(r.Miners[0].NextAfter,P.Data.Text(packet.Body,"next","After"));
            if(!hit)Assert.Equal(r.Miners[0].Position,j.Entry);
            Assert.Single(r.Planner.OwnedJobs);Assert.Equal(r.Miners[0].Id,j.Owner);
        }
        [Fact]
        public void FleetDoesNotAcceptAnotherMinersCompletionReceipt()
        {
            var r=FleetAfterRay();r.Miners[0].NextAfter=r.Planner.Jobs.Last().Id;r.Receive("TELEMETRY",r.Miners[0]);Dispatch(r);
            Assert.DoesNotContain(r.Sent,p=>p.Kind=="JOB" || p.Kind=="NEXT");
        }
        [Fact]
        public void FinishedRegionReturnsWithoutTakingWorkFromADifferentSearchArea()
        {
            var r=FleetAfterRay();foreach(var j in r.Planner.Jobs.ToList())if(!P.TaskPlanner.Finished(j))r.Planner.Report(j,new P.JobReport {Outcome="SurveyEmpty"});
            r.Planner.Survey(Vector3D.Right*3000,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,300);r.Tick(30);Dispatch(r);
            Assert.True(r.Sent.Any(p=>p.Kind=="NEXT"),r.Fleet.Diagnostics+" / last="+r.Planner.Find(r.Miners[0].NextAfter).LastOwner+" / "+r.Fleet.GetTelemetry()[0].ToIni());
            var packet=Assert.Single(r.Sent,p=>p.Kind=="NEXT");Assert.True(P.Data.Flag(packet.Body,"next","Done"));Assert.DoesNotContain(r.Sent,p=>p.Kind=="JOB");
        }
        [Fact]
        public void BusyRegionWaitsInsteadOfBeingReportedComplete()
        {
            var r=FleetAfterRay();foreach(var j in r.Planner.Jobs)if(!P.TaskPlanner.Finished(j))r.Planner.SetOwner(j,"occupied-"+j.Id);
            // Use one outstanding task to keep this scenario within the fleet limit.
            var remaining=r.Planner.Jobs.Where(j=>!P.TaskPlanner.Finished(j)).ToList();foreach(var j in remaining.Skip(1))r.Planner.Report(j,new P.JobReport {Outcome="SurveyEmpty"});
            Dispatch(r);Assert.True(r.Sent.Any(p=>p.Kind=="NEXT"),r.Fleet.Diagnostics+" / last="+r.Planner.Find(r.Miners[0].NextAfter).LastOwner+" / "+r.Fleet.GetTelemetry()[0].ToIni());Assert.False(P.Data.Flag(Assert.Single(r.Sent,p=>p.Kind=="NEXT").Body,"next","Done"));
        }
        [Fact]
        public void FleetCheckpointRetainsCompletionIdentityForAirborneContinuation()
        {
            var before=FleetAfterRay();string saved=before.Checkpoint();var after=new FleetStateTests.Rig(1,false);after.Restore(saved);
            after.Miners[0]=before.Miners[0];after.Receive("HELLO",after.Miners[0]);Dispatch(after);
            Assert.Contains(after.Sent,p=>p.Kind=="JOB" && P.Data.Text(p.Body,"next","After")==before.Miners[0].NextAfter);
        }
    }
}