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;
public bool Reliable;
public MyIni Body=new MyIni();
public string Encode(string fleet)
{
var i=new MyIni();i.Set("wire","Version",2);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 || i.Get("wire","Fleet").ToString()!=fleet)return false;
var p=new Packet { Kind=i.Get("wire","Kind").ToString(),From=i.Get("wire","From").ToString(),Target=i.Get("wire","Target").ToString(),Id=i.Get("wire","Id").ToString(),Session=i.Get("wire","Session").ToString(),Seq=Data.ReadLong(i,"wire","Seq"),Reliable=i.Get("wire","Reliable").ToBoolean() };
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(i.Get("wire","Payload").ToString()))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;
readonly IMyBroadcastListener broadcast;
readonly string tag,session;
readonly Dictionary<string,Dictionary<string,long>> streams=new Dictionary<string,Dictionary<string,long>>();
readonly Dictionary<string,Pending> pending=new Dictionary<string,Pending>();
readonly Dictionary<string,long> peers=new Dictionary<string,long>();
readonly Dictionary<string,string> peerSessions=new Dictionary<string,string>();
readonly HashSet<string> retired=new HashSet<string>();
long sequence;
int retryCursor;
public string Session { get {return session;} }
public int PendingCount { get {return pending.Count;} }
public Wire(Program owner)
{
p=owner;tag="AMS2/"+p.Config.FleetId;session=p.Me.EntityId.ToString()+"-"+DateTime.UtcNow.Ticks.ToString();
broadcast=p.IGC.RegisterBroadcastListener(tag);broadcast.SetMessageCallback("igc");p.IGC.UnicastListener.SetMessageCallback("igc");
}
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.Id=session+"-"+packet.Seq;
if(address==0 && packet.Target.Length>0)peers.TryGetValue(packet.Target,out address);
if(reliable && packet.Target.Length==0)throw new ArgumentException(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)
{
string text=packet.Encode(p.Config.FleetId);
if(text.Length>24000)throw new ArgumentException(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;string key=keys[retryCursor++];Pending 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))
{
MyIGCMessage 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 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)
{
if(retired.Contains(packet.From+"/"+packet.Session))continue;
retired.Add(packet.From+"/"+oldSession);
// 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;
if(packet.Kind=="ACK")
{
string ack=packet.Body.Get("ack","Id").ToString();Pending item;
if(pending.TryGetValue(ack,out item) && item.Packet.Target==packet.From)pending.Remove(ack);continue;
}
Dictionary<string,long> peerStreams;
if(!streams.TryGetValue(packet.From,out peerStreams)) {peerStreams=new Dictionary<string,long>();streams[packet.From]=peerStreams;}
long seen;bool 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));}
}
}
}
}
}
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;
public bool Reliable;
public MyIni Body=new MyIni();
public string Encode(string fleet)
{
var i=new MyIni();i.Set("wire","Version",2);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 || i.Get("wire","Fleet").ToString()!=fleet)return false;
var p=new Packet { Kind=i.Get("wire","Kind").ToString(),From=i.Get("wire","From").ToString(),Target=i.Get("wire","Target").ToString(),Id=i.Get("wire","Id").ToString(),Session=i.Get("wire","Session").ToString(),Seq=Data.ReadLong(i,"wire","Seq"),Reliable=i.Get("wire","Reliable").ToBoolean() };
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(i.Get("wire","Payload").ToString()))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;
readonly IMyBroadcastListener broadcast;
readonly string tag,session;
readonly Dictionary<string,Dictionary<string,long>> streams=new Dictionary<string,Dictionary<string,long>>();
readonly Dictionary<string,Pending> pending=new Dictionary<string,Pending>();
readonly Dictionary<string,long> peers=new Dictionary<string,long>();
readonly Dictionary<string,string> peerSessions=new Dictionary<string,string>();
readonly HashSet<string> retired=new HashSet<string>();
long sequence;
int retryCursor;
public string Session { get {return session;} }
public int PendingCount { get {return pending.Count;} }
public Wire(Program owner)
{
p=owner;tag="AMS2/"+p.Config.FleetId;session=p.Me.EntityId.ToString()+"-"+DateTime.UtcNow.Ticks.ToString();
broadcast=p.IGC.RegisterBroadcastListener(tag);broadcast.SetMessageCallback("igc");p.IGC.UnicastListener.SetMessageCallback("igc");
}
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.Id=session+"-"+packet.Seq;
if(address==0 && packet.Target.Length>0)peers.TryGetValue(packet.Target,out address);
if(reliable && packet.Target.Length==0)throw new ArgumentException(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)
{
string text=packet.Encode(p.Config.FleetId);
if(text.Length>24000)throw new ArgumentException(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;string key=keys[retryCursor++];Pending 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))
{
MyIGCMessage 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 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)
{
if(retired.Contains(packet.From+"/"+packet.Session))continue;
retired.Add(packet.From+"/"+oldSession);
// 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;
if(packet.Kind=="ACK")
{
string ack=packet.Body.Get("ack","Id").ToString();Pending item;
if(pending.TryGetValue(ack,out item) && item.Packet.Target==packet.From)pending.Remove(ack);continue;
}
Dictionary<string,long> peerStreams;
if(!streams.TryGetValue(packet.From,out peerStreams)) {peerStreams=new Dictionary<string,long>();streams[packet.From]=peerStreams;}
long seen;bool 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));}
}
}
}
}
}