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<string> Events=new Queue<string>();
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<Telemetry> GetTelemetry() {return new List<Telemetry> {new Telemetry {Id=P.Config.Id,State=FlightState.Fault,Reason=reason,ReceivedAt=P.Now}};}
}
void DrawInitializationFault()
{
if(dashboard==null || Now<nextDraw)return;
try {dashboard.Draw(new InitializationFault(this,failure));}catch { /* Echo retains the original failure even if screen settings are invalid. */ }
nextDraw=Now+0.5;
}
public void Main(string argument,UpdateType updateSource)
{
double elapsed=Runtime.TimeSinceLastRun.TotalSeconds;
if(!Data.Finite(elapsed)||elapsed<0)elapsed=0;
Now+=Math.Min(elapsed,60);
double dt=Data.Clamp(elapsed,1.0/120,0.2);
try
{
if(argument=="init" && role==null) {Initialize();return;}
if(role==null) {Echo(L.F(storageFailure?L.CommonFixStorage:L.CommonFixConfig,failure));DrawInitializationFault();return;}
#if FLEET
var fleet=role as FleetController;
if(fleet!=null && fleet.Restoring)
{
try
{
bool legacy=fleet.MigratingLegacy;fleet.RestoreStorageStep();
Echo(fleet.Restoring?fleet.RestoreStatus:legacy?L.FleetLegacyMigrationComplete:L.FleetStorageRestored);
if(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<Math.Max(1,Runtime.MaxInstructionCount)*fraction;
}
void DeferBackground()
{
BudgetDeferrals++;
// Discard only replaceable presentation data. Task leases, progress and the
// last complete checkpoint must survive a busy frame.
if(dashboard!=null)dashboard.ClearTransientCache();
if(Now>=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<IMyTerminalBlock>();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));}
}
}
}
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<string> Events=new Queue<string>();
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<Telemetry> GetTelemetry() {return new List<Telemetry> {new Telemetry {Id=P.Config.Id,State=FlightState.Fault,Reason=reason,ReceivedAt=P.Now}};}
}
void DrawInitializationFault()
{
if(dashboard==null || Now<nextDraw)return;
try {dashboard.Draw(new InitializationFault(this,failure));}catch { /* Echo retains the original failure even if screen settings are invalid. */ }
nextDraw=Now+0.5;
}
public void Main(string argument,UpdateType updateSource)
{
double elapsed=Runtime.TimeSinceLastRun.TotalSeconds;
if(!Data.Finite(elapsed)||elapsed<0)elapsed=0;
Now+=Math.Min(elapsed,60);
double dt=Data.Clamp(elapsed,1.0/120,0.2);
try
{
if(argument=="init" && role==null) {Initialize();return;}
if(role==null) {Echo(L.F(storageFailure?L.CommonFixStorage:L.CommonFixConfig,failure));DrawInitializationFault();return;}
#if FLEET
var fleet=role as FleetController;
if(fleet!=null && fleet.Restoring)
{
try
{
bool legacy=fleet.MigratingLegacy;fleet.RestoreStorageStep();
Echo(fleet.Restoring?fleet.RestoreStatus:legacy?L.FleetLegacyMigrationComplete:L.FleetStorageRestored);
if(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<Math.Max(1,Runtime.MaxInstructionCount)*fraction;
}
void DeferBackground()
{
BudgetDeferrals++;
// Discard only replaceable presentation data. Task leases, progress and the
// last complete checkpoint must survive a busy frame.
if(dashboard!=null)dashboard.ClearTransientCache();
if(Now>=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<IMyTerminalBlock>();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));}
}
}
}