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

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
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 RebuiltMinerTests
    {
        // The 13:43/13:46 saves retained the deleted miner's PB and connector,
        // while the replacement used the same logical ID with both entities new.
        const string MinerId="深空工业3号";
        const long OldAddress=73380066387127517,NewAddress=100758657065621036;
        const long OldConnector=95696644539555323,NewConnector=75739440036816648;
        const long Berth=86881199841431342,Epoch=639249584512672040;
        static readonly Vector3D PortPosition=new Vector3D(-67770.011285779881,-123675.8598834305,-162150.0515711247);
        sealed class Delivery { public long Address; public P.Packet Packet; }
        sealed class Rig
        {
            public readonly FleetStateTests.Rig FleetRig=new FleetStateTests.Rig(1,false,"[Dock]\nApproachDistance=130\n[Display]\nOfflineSeconds=5\n");
            public readonly Queue<MyIGCMessage> Incoming=new Queue<MyIGCMessage>();
            public readonly List<Delivery> Sent=new List<Delivery>();
            public P Program=>FleetRig.Program;
            public P.FleetController Fleet=>FleetRig.Fleet;
            public Rig()
            {
                var ports=(List<Stub<IMyShipConnector>>)typeof(FleetStateTests.Rig).GetField("portStubs",BindingFlags.Instance|BindingFlags.NonPublic).GetValue(FleetRig);
                ports[0].Set("EntityId",Berth);FleetRig.SetPort(0,PortPosition,Vector3D.Forward);
                typeof(P.FleetController).GetField("nextWaitingSlot",BindingFlags.Instance|BindingFlags.NonPublic).SetValue(Fleet,2);
                var unicast=new Stub<IMyUnicastListener>().Method("get_HasPendingMessage",a=>Incoming.Count>0).Method("AcceptMessage",a=>Incoming.Dequeue());
                var igc=new Stub<IMyIntergridCommunicationSystem>().Set("UnicastListener",unicast.Value)
                    .Method("SendBroadcastMessage",a=>Capture(0,(string)a[1]))
                    .Method("SendUnicastMessage",a=>{Capture((long)a[0],(string)a[2]);return true;});
                // Keep the Fleet-created Wire and its real AllowRebind callback.
                TestRig.SetBase(Program,"IGC",igc.Value);Program.Bus.Epoch=Epoch;
            }
            object Capture(long address,string text)
            {
                P.Packet packet;Assert.True(P.Packet.TryDecode(text,"mining",out packet));
                Sent.Add(new Delivery {Address=address,Packet=packet});return null;
            }
            public P.Telemetry Miner(bool replacement=true)
            {
                return new P.Telemetry {Id=MinerId,ConnectorId=replacement?NewConnector:OldConnector,State=P.FlightState.Returning,
                    Position=replacement?new Vector3D(-67812.941594717908,-123673.67683211257,-162086.70801528683):new Vector3D(-67606.2248054574,-122641.64258946766,-162455.13400703063),
                    Radius=4.6314600797496155,Battery=1,Hydrogen=1,WaitingSlot=999};
            }
            public P.Packet Request(P.Telemetry miner,bool replacement=true,string kind="DOCK_REQUEST",long sequence=1)
            {
                var address=replacement?NewAddress:OldAddress;var session=replacement?"replacement-pb-session":"deleted-pb-session";
                var packet=new P.Packet {Kind=kind,From=MinerId,Target="base",Session=session,Seq=sequence,Id=session+"-"+kind+"-"+sequence,
                    Epoch=Epoch,Reliable=true,Body=miner.ToIni()};
                Incoming.Enqueue(new MyIGCMessage(packet.Encode("mining"),"AMS2/mining",address));Program.Bus.Drain(Fleet.Receive);return packet;
            }
            public Delivery InitialGrant()
            {
                Request(Miner(false),false);var grant=Assert.Single(Sent,d=>d.Packet.Kind=="DOCK_GRANT");
                Assert.Equal(OldAddress,grant.Address);Assert.Equal(Berth,P.DockFrame.FromIni(grant.Packet.Body).ConnectorId);
                Assert.Equal(2,Assert.Single(Fleet.GetTelemetry()).WaitingSlot);Sent.Clear();return grant;
            }
            public P.Telemetry Registered=>Assert.Single(Fleet.GetTelemetry());
            public Dictionary<string,P.Job> Holds=>(Dictionary<string,P.Job>)typeof(P.FleetController).GetField("clearanceHolds",BindingFlags.Instance|BindingFlags.NonPublic).GetValue(Fleet);
        }

        [Fact]
        public void SavedReplacementReceivesItsExistingBerthDuringTheSameWireDrainAndOldRetriesStop()
        {
            var r=new Rig();var old=r.InitialGrant();var oldFrame=P.DockFrame.FromIni(old.Packet.Body);
            r.Program.Bus.Send("COMMAND",MinerId,new MyIni(),true,OldAddress);var oldCommand=r.Sent.Last().Packet;
            r.Program.Bus.Send("COMMAND","unrelated-miner",new MyIni(),true,4242);Assert.Equal(3,r.Program.Bus.PendingCount);
            r.Sent.Clear();r.Program.Now+=6;var now=r.Program.Now;var request=r.Request(r.Miner());
            var delivered=Assert.Single(r.Sent,d=>d.Packet.Kind=="DOCK_GRANT");var frame=P.DockFrame.FromIni(delivered.Packet.Body);
            Assert.Equal(now,r.Program.Now);Assert.Equal(NewAddress,delivered.Address);Assert.Equal(Berth,frame.ConnectorId);
            Assert.NotEmpty(frame.Token);Assert.NotEqual(oldFrame.Token,frame.Token);Assert.Equal(130,frame.ApproachDistance);
            Assert.Equal(NewAddress,r.Registered.Address);Assert.Equal(NewConnector,r.Registered.ConnectorId);Assert.Equal(2,r.Registered.WaitingSlot);
            Assert.Contains(r.Sent,d=>d.Address==NewAddress && d.Packet.Kind=="ACK" && P.Data.Text(d.Packet.Body,"ack","Id")==request.Id);
            Assert.Equal(1,P.Data.ReadLong(r.FleetRig.Save(),"fleet","LaneCount"));Assert.Equal(2,r.Program.Bus.PendingCount);

            r.Sent.Clear();r.Program.Now+=2;r.Program.Bus.Tick();
            Assert.DoesNotContain(r.Sent,d=>d.Address==OldAddress || d.Packet.Id==old.Packet.Id || d.Packet.Id==oldCommand.Id);
            Assert.Contains(r.Sent,d=>d.Address==NewAddress && d.Packet.Id==delivered.Packet.Id);
            Assert.Contains(r.Sent,d=>d.Address==4242);
        }

        [Fact]
        public void ALiveDifferentShipCannotTakeTheSameIdOrBerth()
        {
            var r=new Rig();var old=r.InitialGrant();r.Program.Now+=4;r.Request(r.Miner());
            Assert.Equal(OldAddress,r.Registered.Address);Assert.Equal(OldConnector,r.Registered.ConnectorId);
            Assert.Empty(r.Sent);Assert.Equal(P.DockFrame.FromIni(old.Packet.Body).Token,P.Data.Text(r.FleetRig.Save(),"fleet","LaneToken"));
            Assert.Equal(1,r.Program.Bus.PendingCount);
        }

        [Fact]
        public void AReplacementProgrammableBlockWithTheSameConnectorCanRecoverWithoutAnOfflineDelay()
        {
            var r=new Rig();var old=r.InitialGrant();var current=r.Miner();current.ConnectorId=OldConnector;
            r.Request(current);var delivery=Assert.Single(r.Sent,d=>d.Packet.Kind=="DOCK_GRANT");
            Assert.Equal(NewAddress,delivery.Address);Assert.Equal(NewAddress,r.Registered.Address);Assert.Equal(2,r.Registered.WaitingSlot);
            Assert.Equal(Berth,P.DockFrame.FromIni(delivery.Packet.Body).ConnectorId);
            Assert.NotEqual(P.DockFrame.FromIni(old.Packet.Body).Token,P.DockFrame.FromIni(delivery.Packet.Body).Token);
        }

        [Theory]
        [InlineData(false)]
        [InlineData(true)]
        public void ARebuiltShipCannotClaimAnOldMiningTaskOrClearanceHold(bool clearanceHold)
        {
            var r=new Rig();r.InitialGrant();
            P.Job held;
            if(clearanceHold)
            {
                held=new P.Job {Id="old-bore-hold",Owner=MinerId,Kind=P.JobKind.Survey,Entry=Vector3D.Forward*1000,Radius=3,Depth=1};
                r.Holds.Add(held.Id,held);
            }
            else
            {
                r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-1000: GPS:inward:0:0:-1001:");
                held=Assert.Single(r.Fleet.GetJobs());r.FleetRig.Planner.SetOwner(held,MinerId);
            }
            r.Sent.Clear();r.Program.Now+=6;r.Request(r.Miner());
            Assert.Equal(OldAddress,r.Registered.Address);Assert.Equal(MinerId,held.Owner);
            Assert.DoesNotContain(r.Sent,d=>d.Address==NewAddress || d.Packet.Kind=="JOB" || d.Packet.Kind=="DOCK_GRANT");
            if(clearanceHold)Assert.Same(held,r.Holds[held.Id]);else Assert.Same(held,r.FleetRig.Planner.OwnedJobs[MinerId]);
        }

        [Theory]
        [InlineData(P.FlightState.Drilling,"")]
        [InlineData(P.FlightState.Returning,"replacement-task")]
        public void ARebuiltShipMustBeReturningWithoutATask(P.FlightState state,string job)
        {
            var r=new Rig();r.InitialGrant();r.Program.Now+=6;var replacement=r.Miner();replacement.State=state;replacement.JobId=job;
            r.Request(replacement);Assert.Equal(OldAddress,r.Registered.Address);Assert.Empty(r.Sent);
        }

        [Fact]
        public void RestoredObsoleteRegistrationAndBerthCanRecoverToTheRebuiltShip()
        {
            var before=new Rig();var old=before.InitialGrant();var storage=before.FleetRig.Checkpoint();
            var after=new Rig();after.FleetRig.Restore(storage);Assert.Equal(OldAddress,after.Registered.Address);
            Assert.Empty(P.Data.Text(after.FleetRig.Save(),"fleet","LaneToken"));after.Program.Now+=6;
            after.Request(after.Miner());var delivery=Assert.Single(after.Sent,d=>d.Packet.Kind=="DOCK_GRANT");
            var frame=P.DockFrame.FromIni(delivery.Packet.Body);Assert.Equal(NewAddress,delivery.Address);Assert.Equal(Berth,frame.ConnectorId);
            Assert.Equal(2,after.Registered.WaitingSlot);Assert.Equal(NewAddress,after.Registered.Address);
            Assert.NotEqual(P.DockFrame.FromIni(old.Packet.Body).Token,frame.Token);Assert.NotEmpty(frame.Token);
            var saved=after.FleetRig.Save();Assert.Equal(1,P.Data.ReadLong(saved,"fleet","LaneCount"));Assert.Equal(0,P.Data.ReadLong(saved,"fleet","ProtectedOwnerCount"));
        }
    }
}