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

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;

namespace AutoMiningScript
{
    public partial class Program : MyGridProgram
    {
        public class Packet
        {
            public string Kind = "", From = "", Target = "", Id = "", Session = "";
            public long Seq, Source, Epoch;
            public bool Reliable;
            public MyIni Body = new MyIni();
#if FLEET || TESTS
            public bool IsPortRequest => Kind=="DOCK_REQUEST" || Kind=="DEPART_REQUEST";
            public bool IsRegistration => Kind=="HELLO" || Kind=="TELEMETRY" || IsPortRequest;
#endif
            public string Encode(string fleet)
            {
                var i = new MyIni(); i.Set("wire", "Version", 2); i.Set("wire", "Epoch", Epoch); i.Set("wire", "Fleet", fleet); i.Set("wire", "Kind", Kind); i.Set("wire", "From", From); i.Set("wire", "Target", Target); i.Set("wire", "Id", Id); i.Set("wire", "Session", Session); i.Set("wire", "Seq", Seq); i.Set("wire", "Reliable", Reliable); i.Set("wire", "Payload", Body.ToString()); return i.ToString();
            }
            public static bool TryDecode(string text, string fleet, out Packet packet)
            {
                packet = null; if (text == null || text.Length > 24000) return false;
                try
                {
                    var i = new MyIni(); if (!i.TryParse(text) || Data.ReadLong(i, "wire", "Version") != 2 || Data.Text(i,"wire", "Fleet") != fleet) return false;
                    var p = new Packet { Kind = Data.Text(i,"wire", "Kind"), From = Data.Text(i,"wire", "From"), Target = Data.Text(i,"wire", "Target"), Id = Data.Text(i,"wire", "Id"), Session = Data.Text(i,"wire", "Session"), Seq = Data.ReadLong(i, "wire", "Seq"), Reliable = Data.Flag(i,"wire", "Reliable") };
                    if (p.From.Length == 0 || p.From.Length > 64 || p.Kind.Length == 0 || p.Kind.Length > 32 || p.Id.Length > 160 || p.Session.Length == 0 || p.Session.Length > 160 || p.Seq < 1 || !p.Body.TryParse(Data.Text(i,"wire", "Payload"))) return false;
                    p.Epoch=Data.ReadLong(i,"wire","Epoch");if(p.Epoch<0 || p.Epoch>DateTime.MaxValue.Ticks)return false;
                    packet = p; return true;
                }
                catch (ArgumentException) { return false; }
            }
        }
        public class Wire
        {
            class Pending { public Packet Packet; public long Address; public double Sent; public int Tries; }
            readonly Program p;
            IMyBroadcastListener broadcast;
            string tag, session;
            readonly Dictionary<string, Dictionary<string, long>> streams = Data.CreateDictionary<string, Dictionary<string, long>>();
            readonly Dictionary<string, Pending> pending = Data.CreateDictionary<string, Pending>();
            readonly Dictionary<string, long> peers = Data.CreateDictionary<string, long>();
            readonly Dictionary<string, long> protectedPeers = Data.CreateDictionary<string, long>();
            readonly Dictionary<string, string> peerSessions = Data.CreateDictionary<string, string>();
            readonly HashSet<string> retired = new HashSet<string>();
            readonly Queue<string> retiredOrder = new Queue<string>();
            readonly Queue<string> forgotten = new Queue<string>();
            long sequence;
            int retryCursor;
            public string Session => session;
            public long Epoch;
            public int PendingCount => pending.Count;
#if FLEET || TESTS
            public Func<Packet,bool> AllowRebind;
#endif
            public void ProtectPeer(string id, long address)
            { if (address != 0) protectedPeers[id] = address; else protectedPeers.Remove(id); }
            public void CancelPending(string kind, string target = null)
            {
                var keys = new List<string>(pending.Keys);
                foreach (var key in keys)
                {
                    var packet = pending[key].Packet;
                    if ((kind == null || packet.Kind == kind) && (target == null || packet.Target == target)) pending.Remove(key);
                }
            }
            public void ForgetPeer(string id)
            {
                CancelPending(null, id); peers.Remove(id);
                // Keep a bounded replay window after releasing the address slot. New
                // telemetry can register again; already processed packets cannot do so.
                forgotten.Enqueue(id);
                while (forgotten.Count > 128)
                {
                    var old = forgotten.Dequeue();
                    if (peers.ContainsKey(old) || forgotten.Contains(old)) continue;
                    streams.Remove(old); peerSessions.Remove(old);
                }
            }
            public Wire(Program owner)
            {
                p = owner;Reset();
            }
            public void Reset()
            {
                var channel="AMS2/"+p.Config.FleetId;
                if(broadcast==null || tag!=channel)
                {
                    if(broadcast!=null)p.IGC.DisableBroadcastListener(broadcast);
                    tag=channel;broadcast=p.IGC.RegisterBroadcastListener(tag);broadcast.SetMessageCallback("igc");p.IGC.UnicastListener.SetMessageCallback("igc");
                }
                pending.Clear();peers.Clear();protectedPeers.Clear();streams.Clear();peerSessions.Clear();retired.Clear();retiredOrder.Clear();forgotten.Clear();retryCursor=0;sequence=0;
                session=p.Me.EntityId.ToString()+"-"+DateTime.UtcNow.Ticks.ToString();Epoch=0;
            }
            public void Send(string kind, string target, MyIni body, bool reliable = false, long address = 0)
            {
                var packet = new Packet { Kind = kind, From = p.Config.Id, Target = target ?? "", Session = session, Seq = ++sequence, Body = body ?? new MyIni(), Reliable = reliable };
                packet.Epoch=Epoch;
                packet.Id = session + "-" + packet.Seq;
                if (address == 0 && packet.Target.Length > 0) peers.TryGetValue(packet.Target, out address);
                if (reliable && packet.Target.Length == 0) throw Data.Invalid(L.CommonReliableTarget);
                if (reliable && pending.Count >= 64) { p.Log(L.F(L.CommonReliableQueue, kind)); return; }
                Transmit(packet, address);
                if (reliable) pending[packet.Id] = new Pending { Packet = packet, Address = address, Sent = p.Now, Tries = 1 };
            }
            void Transmit(Packet packet, long address)
            {
                var text = packet.Encode(p.Config.FleetId);
                if (text.Length > 24000) throw Data.Invalid(L.CommonMessageBudget);
                if (address != 0) p.IGC.SendUnicastMessage(address, tag, text); else p.IGC.SendBroadcastMessage(tag, text, TransmissionDistance.TransmissionDistanceMax);
            }
            public void Tick()
            {
                if (!p.HasBudget(0.55)) return;
                var keys = new List<string>(pending.Keys);
                int sent = 0;
                for (int n = 0; n < keys.Count && sent < 2 && p.HasBudget(0.6); n++)
                {
                    retryCursor %= keys.Count; var key = keys[retryCursor++]; var item = pending[key];
                    if (p.Now - item.Sent < 1.5) continue;
                    if (item.Tries >= 6) { p.Log(L.F(L.CommonNoAck, item.Packet.Kind, item.Packet.Target)); pending.Remove(key); continue; }
                    if (item.Address == 0) peers.TryGetValue(item.Packet.Target, out item.Address);
                    Transmit(item.Packet, item.Address); item.Sent = p.Now; item.Tries++; sent++;
                }
            }
            public void Drain(Action<Packet> receive)
            {
                int budget = 4, start = p.Runtime.CurrentInstructionCount;
                while (budget-- > 0 && p.HasBudget(0.55) && p.Runtime.CurrentInstructionCount - start < p.Runtime.MaxInstructionCount * 0.2 && (broadcast.HasPendingMessage || p.IGC.UnicastListener.HasPendingMessage))
                {
                    var message = p.IGC.UnicastListener.HasPendingMessage ? p.IGC.UnicastListener.AcceptMessage() : broadcast.AcceptMessage();
                    if (message.Tag != tag) continue;
                    Packet packet; if (!Packet.TryDecode(message.Data as string, p.Config.FleetId, out packet) || packet.From == p.Config.Id || (packet.Target.Length > 0 && packet.Target != p.Config.Id)) continue;
                    packet.Source = message.Source;
                    long protectedAddress;
#if FLEET || TESTS
                    long knownAddress;
                    bool addressConflict=(protectedPeers.TryGetValue(packet.From,out protectedAddress) && protectedAddress!=packet.Source) ||
                        (peers.TryGetValue(packet.From,out knownAddress) && knownAddress!=packet.Source);
#else
                    if (protectedPeers.TryGetValue(packet.From, out protectedAddress) && protectedAddress != packet.Source) continue;
#endif
                    if(p.Config.Role=="fleet" && packet.Epoch!=Epoch)
                    {
#if FLEET || TESTS
                        if(addressConflict)continue;
#endif
                        try {receive(packet);}catch(ArgumentException e){p.Log(L.F(L.CommonRejectedPacket,packet.Kind,e.Message));}
                        continue;
                    }
                    if(packet.From==p.Config.BaseId && packet.Epoch<Epoch)continue;
                    if (retired.Contains(packet.From + "/" + packet.Session)) continue;
                    if (packet.Kind == "ACK")
                    {
#if FLEET || TESTS
                        if(addressConflict)continue;
#endif
                        var ack = Data.Text(packet.Body,"ack", "Id"); Pending item; long peerAddress;
                        if (pending.TryGetValue(ack, out item) && item.Packet.Target == packet.From &&
                            (item.Address == 0 || item.Address == message.Source) &&
                            (!peers.TryGetValue(packet.From, out peerAddress) || peerAddress == message.Source)) pending.Remove(ack);
                        // ACKs confirm sends; they never register a forgotten identity or
                        // replace the session of a miner that is reconnecting.
                        continue;
                    }
#if FLEET || TESTS
                    if(addressConflict)
                    {
                        // Registration-shaped packets must pass the existing epoch and
                        // replay gates before fleet geometry may authorize a new address.
                        if(packet.Epoch!=Epoch || AllowRebind==null ||
                            !packet.IsRegistration ||
                            (peers.Count>=64 && !peers.ContainsKey(packet.From)))continue;
                        string currentSession;Dictionary<string,long> currentStreams;long previousSequence;
                        if(peerSessions.TryGetValue(packet.From,out currentSession) && currentSession==packet.Session && streams.TryGetValue(packet.From,out currentStreams) &&
                            (currentStreams.TryGetValue(packet.Kind,out previousSequence)?packet.Seq<=previousSequence:currentStreams.Count>=32))continue;
                        bool allowed=false;
                        try {allowed=AllowRebind(packet);}catch(ArgumentException e){p.Log(L.F(L.CommonRejectedPacket,packet.Kind,e.Message));}
                        if(!allowed)continue;
                        // Keep protection active at the approved address. The normal
                        // session path below retires the former session before delivery.
                        CancelPending(null,packet.From);peers[packet.From]=packet.Source;
                        if(protectedPeers.ContainsKey(packet.From))protectedPeers[packet.From]=packet.Source;
                    }
#endif
                    string forgottenSession; Dictionary<string, long> forgottenStreams; long forgottenSeq;
                    if (!peers.ContainsKey(packet.From) && peerSessions.TryGetValue(packet.From, out forgottenSession) && forgottenSession == packet.Session &&
                        streams.TryGetValue(packet.From, out forgottenStreams) && forgottenStreams.TryGetValue(packet.Kind, out forgottenSeq) && packet.Seq <= forgottenSeq)
                    {
                        if (packet.Reliable) { var ack = new MyIni(); ack.Set("ack", "Id", packet.Id); Send("ACK", packet.From, ack, false, message.Source); }
                        continue;
                    }
                    long known;
                    if (peers.TryGetValue(packet.From, out known) && known != message.Source) { p.Log(L.F(L.CommonDuplicateNode, packet.From)); continue; }
                    if (peers.Count >= 64 && !peers.ContainsKey(packet.From)) continue;
                    string oldSession;
                    if (peerSessions.TryGetValue(packet.From, out oldSession) && oldSession != packet.Session)
                    {
                        var retiredKey = packet.From + "/" + oldSession;
                        if (retired.Add(retiredKey)) retiredOrder.Enqueue(retiredKey);
                        while (retiredOrder.Count > 1024) retired.Remove(retiredOrder.Dequeue());
                        // Session replacement automatically drops obsolete sequence-cache entries.
                        // Retired session identities still reject late/replayed old packets.
                        streams.Remove(packet.From);
                    }
                    peers[packet.From] = message.Source; peerSessions[packet.From] = packet.Session;
                    Dictionary<string, long> peerStreams;
                    if (!streams.TryGetValue(packet.From, out peerStreams)) { peerStreams = Data.CreateDictionary<string, long>(); streams[packet.From] = peerStreams; }
                    long seen; var duplicate = peerStreams.TryGetValue(packet.Kind, out seen) && packet.Seq <= seen;
                    if (!duplicate && peerStreams.Count >= 32 && !peerStreams.ContainsKey(packet.Kind)) { p.Log(L.CommonStreamLimit); continue; }
                    if (packet.Reliable) { var ack = new MyIni(); ack.Set("ack", "Id", packet.Id); Send("ACK", packet.From, ack, false, message.Source); }
                    if (duplicate) continue;
                    peerStreams[packet.Kind] = packet.Seq;
                    try { receive(packet); } catch (ArgumentException e) { p.Log(L.F(L.CommonRejectedPacket, packet.Kind, e.Message)); }
                }
            }
        }
    }
}