using System; using System.Collections.Generic; using Sandbox.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame.Utilities; namespace AutoMiningScript { public partial class Program : MyGridProgram { public Settings Config; public Wire Bus; public double Now; public readonly Queue Events=new Queue(); RoleLogic role; Dashboard dashboard; string failure=""; bool storageFailure; double nextDraw,nextSave,nextBudgetNotice; public int BudgetDeferrals {get;private set;} #if !FLEET MyIni checkpoint; #endif public const string Version="2.0.0"; public Program() { Runtime.UpdateFrequency=UpdateFrequency.Update1; Initialize(); } static string CompiledRole() { #if FLEET return "fleet"; #elif MARKER return "marker"; #elif WATCHDOG return "watchdog"; #else return "miner"; #endif } void Initialize() { dashboard=null;role=null;storageFailure=false; try { string defaultRole=CompiledRole();string id=defaultRole=="fleet"?"base":defaultRole+"-"+Me.EntityId; if(string.IsNullOrWhiteSpace(Me.CustomData))Me.CustomData=Settings.Template(defaultRole,id); Config=new Settings(Me.CustomData,defaultRole,id);dashboard=new Dashboard(this);Bus=new Wire(this); #if FLEET role=new FleetController(this); #elif MARKER role=new MarkerController(this); #elif WATCHDOG role=new WatchdogController(this); #else role=new MinerController(this); #endif failure=""; storageFailure=true;Restore();storageFailure=false; #if FLEET if(((FleetController)role).Restoring)Log(((FleetController)role).RestoreStatus); else #endif Log(L.F(L.CommonReady,L.Brand,Version,Config.Id)); } catch(Exception e) {failure=e.Message;SafeClear();role=null;Echo(L.F(L.CommonInitError,failure));DrawInitializationFault();} } // Hardware self-check errors still leave a useful local console after INI parsing. sealed class InitializationFault : RoleLogic { readonly string reason; public InitializationFault(Program p,string message):base(p) {reason=message;} public override string Diagnostics {get{return L.F(L.CommonInitError,reason);}} public override List GetTelemetry() {return new List {new Telemetry {Id=P.Config.Id,State=FlightState.Fault,Reason=reason,ReceivedAt=P.Now}};} } void DrawInitializationFault() { if(dashboard==null || Now=nextDraw&&HasBudget(0.3)) {dashboard.Draw(fleet);nextDraw=Now+0.5;} } catch(Exception e) {storageFailure=true;failure=L.F(L.CommonStorageInvalid,e.Message);role=null;Echo(L.F(L.CommonFixStorage,failure));} return; } #endif if(!string.IsNullOrWhiteSpace(argument) && argument!="igc") { if(argument=="init") {role.Command("rescan");Log(L.CommonConfigReload);} else if(argument.StartsWith("page",StringComparison.OrdinalIgnoreCase))dashboard.Command(argument); else role.Command(argument.Trim()); } // Physics runs before optional communications, drawing and persistence. // The engine resets its counter on the next invocation; it cannot be reset here. #if WATCHDOG // Heartbeats already queued for this frame must precede timeout evaluation. Bus.Drain(role.Receive); #endif role.Tick(dt); if(!HasBudget()) {DeferBackground();return;} #if !WATCHDOG Bus.Drain(role.Receive); #endif if(!HasBudget()) {DeferBackground();return;} Bus.Tick(); if(!HasBudget()) {DeferBackground();return;} dashboard.Update(role); if(Now>=nextDraw && HasBudget(0.45)) { dashboard.Draw(role);nextDraw=Now+0.5; if(HasBudget())Echo(L.F(L.CommonRuntime,L.Brand,Version,Config.Id,role.Diagnostics,Runtime.CurrentInstructionCount,Runtime.MaxInstructionCount)); } if(!HasBudget()) {DeferBackground();return;} if(Now>=nextSave && HasBudget(0.35))Checkpoint(); } catch(Exception e) { failure=e.Message;Log(L.F(L.CommonFault,failure)); try {if(role!=null)role.Emergency(failure);else SafeClear();}catch {SafeClear();} Echo(L.F(L.CommonFault,failure)); } } public bool HasBudget(double fraction=0.65) { return Runtime.CurrentInstructionCount=nextBudgetNotice && HasBudget(0.85)) { Log(L.CommonBudgetDeferred);nextBudgetNotice=Now+10; } } public void Log(string text) { if(text==null)return;if(text.Length>240)text=text.Substring(0,240); Events.Enqueue(Now.ToString("F1",Data.Culture)+" "+text);while(Events.Count>12)Events.Dequeue(); } public void SafeClear() { #if !FLEET && !MARKER var blocks=new List();GridTerminalSystem.GetBlocksOfType(blocks,b=>b.CubeGrid==Me.CubeGrid); foreach(var block in blocks) { var thrust=block as IMyThrust;if(thrust!=null)thrust.ThrustOverridePercentage=0; var gyro=block as IMyGyro;if(gyro!=null) {gyro.Pitch=0;gyro.Yaw=0;gyro.Roll=0;gyro.GyroOverride=false;} var drill=block as IMyShipDrill;if(drill!=null)drill.Enabled=false; var control=block as IMyShipController;if(control!=null)control.DampenersOverride=true; var remote=block as IMyRemoteControl;if(remote!=null)remote.SetAutoPilotEnabled(false); } #endif } public void Save() { if(role==null)return; #if !FLEET // Small local state can be committed synchronously at world-save/calibration. var ini=NewCheckpoint();role.Save(ini);Commit(ini); #endif // Fleet Storage is the last fully completed checkpoint. Never serialize thousands // of jobs in a world-save callback or overwrite it with an incomplete snapshot. } MyIni NewCheckpoint() {var ini=new MyIni();ini.Set("storage","Version",2);ini.Set("storage","Role",Config.Role);ini.Set("storage","Id",Config.Id);return ini;} void Checkpoint() { #if FLEET string value; if(!((FleetController)role).CheckpointStorage(out value))return; if(value.Length<=1500000)Storage=value;else Log(L.CommonStorageBudget); nextSave=Now+2; #else if(checkpoint==null)checkpoint=NewCheckpoint(); if(!role.Checkpoint(checkpoint))return; Commit(checkpoint);checkpoint=null;nextSave=Now+2; #endif } void Commit(MyIni ini) { string value=ini.ToString();if(value.Length<=1500000)Storage=value;else Log(L.CommonStorageBudget); } void Restore() { if(string.IsNullOrWhiteSpace(Storage))return; #if FLEET if(((FleetController)role).BeginRestoreStorage(Storage))return; #endif var ini=new MyIni(); if(!ini.TryParse(Storage) || Data.ReadLong(ini,"storage","Version")!=2 || ini.Get("storage","Role").ToString()!=Config.Role || ini.Get("storage","Id").ToString()!=Config.Id) {Log(L.CommonStorageIncompatible);return;} try {role.Load(ini);}catch(Exception e) {role.Emergency(L.F(L.CommonStorageInvalid,e.Message));} } } }