diff --git a/AirMonitor/AirZone.cs b/AirMonitor/AirZone.cs index 9d4c3df..3cd6586 100644 --- a/AirMonitor/AirZone.cs +++ b/AirMonitor/AirZone.cs @@ -21,11 +21,11 @@ namespace IngameScript private const float TriggerLevel = 0.75F; - public AirZone(string zoneName, MyIni ini, IConsole console) + public AirZone(string zoneName, IConsoleProgram program) { Name = zoneName; - _ini = ini; - _console = new PrefixedConsole(console, zoneName); + _ini = program.Ini; + _console = new PrefixedConsole(program.Console, zoneName); _sequencer = new Sequencer(zoneName, _console); Vents = new List(); _doors = new List(); diff --git a/AirMonitor/Program.cs b/AirMonitor/Program.cs index 0d2156b..bae82a1 100644 --- a/AirMonitor/Program.cs +++ b/AirMonitor/Program.cs @@ -1,32 +1,18 @@ -using Sandbox.Game.EntityComponents; -using Sandbox.ModAPI.Ingame; -using Sandbox.ModAPI.Interfaces; +using Sandbox.ModAPI.Ingame; using SpaceEngineers.Game.ModAPI.Ingame; -using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Text; -using VRage; -using VRage.Collections; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.GUI.TextPanel; -using VRage.Game.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame.Utilities; -using VRage.Game.ObjectBuilders.Definitions; -using VRageMath; namespace IngameScript { - public partial class Program : MyGridProgram + public partial class Program : MyGridProgram, IConsoleProgram { - private MyIni _ini; - private Console _console; - private MyCommandLine _cli; - private int _totalTicks = 0; + public MyIni Ini { get; private set; } + public IConsole Console { get; private set; } + private MyCommandLine _cli; private Dictionary _zones; private List> _jobs; private List _displays; @@ -35,9 +21,10 @@ namespace IngameScript public Program() { + Ini = new MyIni(); + Console = new MainConsole(this, "Air Pressure Monitor"); + _cli = new MyCommandLine(); - _ini = new MyIni(); - _console = new Console(this, _ini); _zones = new Dictionary(); _jobs = new List>(); _displays = new List(); @@ -49,7 +36,7 @@ namespace IngameScript GridTerminalSystem.GetBlocksOfType(blocks, block => MyIni.HasSection(block.CustomData, "airMonitor")); foreach (IMyTerminalBlock block in blocks) { - _ini.TryParse(block.CustomData); + Ini.TryParse(block.CustomData); string[] zones = new string[] { }; // TODO: how do we display text on e.g. decorative console blocks? @@ -58,16 +45,16 @@ namespace IngameScript // It'd probably be safe to just check SurfaceCount... experiment with this later if (block is IMyTextSurface) { - _console.Print($"Adding monitoring display '{block.CustomName}'"); + Console.Print($"Adding monitoring display '{block.CustomName}'"); _displays.Add(block as IMyTextSurface); } - if (_ini.Get("airMonitor", "display").ToString() != "") + if (Ini.Get("airMonitor", "display").ToString() != "") { - int displayIndex = _ini.Get("airMonitor", "display").ToInt32(); + int displayIndex = Ini.Get("airMonitor", "display").ToInt32(); IMyTextSurfaceProvider provider = block as IMyTextSurfaceProvider; if (provider.SurfaceCount <= displayIndex) { - _console.Print($"Invalid display index '{displayIndex}' in block '{block.CustomName}'"); + Console.Print($"Invalid display index '{displayIndex}' in block '{block.CustomName}'"); } else { @@ -79,30 +66,30 @@ namespace IngameScript _oxygenTanks.Add(block as IMyGasTank); } - if (_ini.Get("airMonitor", "zones").ToString() != "") + if (Ini.Get("airMonitor", "zones").ToString() != "") { - zones = _ini.Get("airMonitor", "zones").ToString().Split(','); + zones = Ini.Get("airMonitor", "zones").ToString().Split(','); } - else if (_ini.Get("airMonitor", "zone").ToString() != "") + else if (Ini.Get("airMonitor", "zone").ToString() != "") { - zones = new string[] { _ini.Get("airMonitor", "zone").ToString() }; + zones = new string[] { Ini.Get("airMonitor", "zone").ToString() }; } foreach (string zone in zones) { if (!_zones.ContainsKey(zone)) { - _zones[zone] = new AirZone(zone, _ini, _console); + _zones[zone] = new AirZone(zone, this); } _zones[zone].AddBlock(block); } } - _console.Print($"Found {_zones.Count} zones:"); + Console.Print($"Found {_zones.Count} zones:"); foreach (KeyValuePair kvp in _zones) { AirZone zone = kvp.Value; - _console.Print(kvp.Key); + Console.Print(kvp.Key); _jobs.Add(zone.Monitor()); } Runtime.UpdateFrequency |= UpdateFrequency.Update100; @@ -110,7 +97,8 @@ namespace IngameScript public void Main(string argument, UpdateType updateSource) { - _console.PrintLower($"Air Monitor\nTotal Ticks: {++_totalTicks}"); + Console.UpdateTickCount(); + if (argument != "") { _cli.TryParse(argument); @@ -119,7 +107,7 @@ namespace IngameScript for (int i = 0; i < _cli.ArgumentCount; i++) { string zone = _cli.Argument(i); - _console.Print($"Resetting {zone}."); + Console.Print($"Resetting {zone}."); if (_zones.ContainsKey(zone)) { _zones[zone].Reset(); @@ -128,41 +116,43 @@ namespace IngameScript } } - // write diagnostics - if (_displays.Count != 0) - { - _displayBuffer.Clear(); - _displayBuffer.Append("AIR PRESSURE REPORT\n\n"); - foreach (AirZone zone in _zones.Values) - { - _displayBuffer.Append(zone.Name); - _displayBuffer.Append(": "); - _displayBuffer.Append(zone.Triggered ? "ALARM TRIPPED " : "NOMINAL "); - foreach (IMyAirVent vent in zone.Vents) - { - _displayBuffer.Append((int)(vent.GetOxygenLevel() * 100F)); - _displayBuffer.Append("% "); - } - - _displayBuffer.Append("\n"); - } - - _displayBuffer.Append("\n"); - _displayBuffer.Append("OXYGEN TANK LEVELS\n"); - foreach (IMyGasTank tank in _oxygenTanks) - { - _displayBuffer.Append((int)(tank.FilledRatio * 100)); - _displayBuffer.Append("% "); - } - - foreach (IMyTextSurface display in _displays) display.WriteText(_displayBuffer.ToString()); - } - foreach (IEnumerator job in _jobs) { if (job.MoveNext()) continue; - _console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); + Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); } } + + // write diagnostics to any configured display screens + private void _updateDisplays() + { + if (_displays.Count == 0) return; + + _displayBuffer.Clear(); + _displayBuffer.Append("AIR PRESSURE REPORT\n\n"); + foreach (AirZone zone in _zones.Values) + { + _displayBuffer.Append(zone.Name); + _displayBuffer.Append(": "); + _displayBuffer.Append(zone.Triggered ? "ALARM TRIPPED " : "NOMINAL "); + foreach (IMyAirVent vent in zone.Vents) + { + _displayBuffer.Append((int)(vent.GetOxygenLevel() * 100F)); + _displayBuffer.Append("% "); + } + + _displayBuffer.Append("\n"); + } + + _displayBuffer.Append("\n"); + _displayBuffer.Append("OXYGEN TANK LEVELS\n"); + foreach (IMyGasTank tank in _oxygenTanks) + { + _displayBuffer.Append((int)(tank.FilledRatio * 100)); + _displayBuffer.Append("% "); + } + + foreach (IMyTextSurface display in _displays) display.WriteText(_displayBuffer.ToString()); + } } } diff --git a/Airlock/Airlock.cs b/Airlock/Airlock.cs index ca64240..868b4d3 100644 --- a/Airlock/Airlock.cs +++ b/Airlock/Airlock.cs @@ -86,11 +86,11 @@ namespace IngameScript private const int CooldownTicks = 120; private const int SealTimeoutTicks = 30; - public Airlock(MyIni ini, Console console, string name) + public Airlock(string name, IConsoleProgram _program) { - _ini = ini; + _ini = _program.Ini; _name = name; - _console = console.CreatePrefixedConsole(_name); + _console = new PrefixedConsole(_program.Console, _name); _lights = new List(); // _displays = new List(); } diff --git a/Airlock/Program.cs b/Airlock/Program.cs index 807cab0..2135af5 100644 --- a/Airlock/Program.cs +++ b/Airlock/Program.cs @@ -5,20 +5,19 @@ using VRage.Game.ModAPI.Ingame.Utilities; namespace IngameScript { - public partial class Program : MyGridProgram + public partial class Program : MyGridProgram, IConsoleProgram { + public IConsole Console { get; private set; } + public MyIni Ini { get; private set; } + private Dictionary _airlocks; private List> _jobs; - - private int _tickCount = 0; private MyCommandLine _cli; - private Console _console; - private MyIni _ini; public Program() { - _ini = new MyIni(); - _console = new Console(this, _ini); + Ini = new MyIni(); + Console = new MainConsole(this, "Airlock Controller"); _cli = new MyCommandLine(); _jobs = new List>(); _airlocks = new Dictionary(); @@ -28,25 +27,26 @@ namespace IngameScript IMyAirVent referenceVent = null; foreach (IMyTerminalBlock block in airlockBlocks) { - _ini.TryParse(block.CustomData, "airlock"); + Ini.TryParse(block.CustomData, "airlock"); // TODO: redundant reference vents would be awesome. Everyone loves redundancy - if (block is IMyAirVent && _ini.Get("airlock", "reference").ToBoolean()) + if (block is IMyAirVent && Ini.Get("airlock", "reference").ToBoolean()) { - if (referenceVent != null) { - _console.Print("Found multiple reference vents. Only the first one will be used."); + if (referenceVent != null) + { + Console.Print("Found multiple reference vents. Only the first one will be used."); continue; } referenceVent = block as IMyAirVent; - _console.Print($"Found reference vent {block.CustomName}."); + Console.Print($"Found reference vent {block.CustomName}."); continue; } - string airlockName = _ini.Get("airlock", "id").ToString(); + string airlockName = Ini.Get("airlock", "id").ToString(); if (!_airlocks.ContainsKey(airlockName)) { - _airlocks[airlockName] = new Airlock(_ini, _console, airlockName); + _airlocks[airlockName] = new Airlock(airlockName, this); } _airlocks[airlockName].AddBlock(block); @@ -54,23 +54,23 @@ namespace IngameScript if (referenceVent != null) foreach (Airlock airlock in _airlocks.Values) { airlock.ReferenceVent = referenceVent; } - _console.Print($"Found {_airlocks.Count} airlocks."); - _console.PrintLower($"Airlock Controller\nTotal Ticks: 0"); + Console.Print($"Found {_airlocks.Count} airlocks."); + Console.PrintLower($"Airlock Controller\nTotal Ticks: 0"); } public void Main(string argument, UpdateType updateSource) { - _console.PrintLower($"Airlock Controller\nTotal Ticks: {++_tickCount}"); + Console.UpdateTickCount(); if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal) { _cli.TryParse(argument); - if (_cli.ArgumentCount == 0) { _console.Print("You must provide an airlock ID."); } + if (_cli.ArgumentCount == 0) { Console.Print("You must provide an airlock ID."); } else { string airlockName = _cli.Argument(0); - if (!_airlocks.ContainsKey(airlockName)) _console.Print($"Airlock ID '{airlockName}' not found."); - else if (!_airlocks[airlockName].Functional) _console.Print($"Airlock '{airlockName}' is not functional."); + if (!_airlocks.ContainsKey(airlockName)) Console.Print($"Airlock ID '{airlockName}' not found."); + else if (!_airlocks[airlockName].Functional) Console.Print($"Airlock '{airlockName}' is not functional."); else { _jobs.Add(_airlocks[airlockName].CycleAirlock()); @@ -86,7 +86,7 @@ namespace IngameScript job.Dispose(); _jobs.Remove(job); i--; - _console.Print("Job Removed From Queue."); + Console.Print("Job Removed From Queue."); } if (_jobs.Count == 0) Runtime.UpdateFrequency = UpdateFrequency.None; diff --git a/MechanicalDoor/Program.cs b/MechanicalDoor/Program.cs index b4ba0fe..158bc9d 100644 --- a/MechanicalDoor/Program.cs +++ b/MechanicalDoor/Program.cs @@ -5,21 +5,19 @@ using VRage.Game.ModAPI.Ingame.Utilities; namespace IngameScript { - public partial class Program : MyGridProgram + public partial class Program : MyGridProgram, IConsoleProgram { private MyCommandLine _cli; - private MyIni _ini; - private Console _console; + public MyIni Ini { get; private set; } + public IConsole Console { get; private set; } private List> _jobs; private Dictionary _doors; - private int _tickCount; public Program() { - _tickCount = 0; _cli = new MyCommandLine(); - _ini = new MyIni(); - _console = new Console(this, _ini); + Ini = new MyIni(); + Console = new MainConsole(this, "Door Controller"); _jobs = new List>(); _doors = new Dictionary(); @@ -27,27 +25,27 @@ namespace IngameScript GridTerminalSystem.GetBlocksOfType(doorBlocks, block => MyIni.HasSection(block.CustomData, "mechDoor")); foreach (IMyTerminalBlock block in doorBlocks) { - _ini.TryParse(block.CustomData); - string doorName = _ini.Get("mechDoor", "id").ToString(); + Ini.TryParse(block.CustomData); + string doorName = Ini.Get("mechDoor", "id").ToString(); // Create the door if this is a new id if (!_doors.ContainsKey(doorName)) { - _doors[doorName] = new Sequencer(doorName, new PrefixedConsole(_console, doorName)); + _doors[doorName] = new Sequencer(doorName, new PrefixedConsole(Console, doorName)); } // Add the part; the Door object handles typing and sequencing. - ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, _ini, "mechDoor"); + ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, Ini, "mechDoor"); if (!(block is IMyShipMergeBlock)) wrapped.Step = 1; // TODO: actually support merge blocks here - if (wrapped == null) { _console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; } + if (wrapped == null) { Console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; } _doors[doorName].AddBlock(wrapped); } - _console.Print($"Found {_doors.Keys.Count} doors."); + Console.Print($"Found {_doors.Keys.Count} doors."); } public void Main(string argument, UpdateType updateSource) { - _console.PrintLower($"Total Ticks: {_tickCount++}"); + Console.UpdateTickCount(); if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal) { @@ -57,7 +55,7 @@ namespace IngameScript if (_cli.ArgumentCount == 0) { - _console.Print("No arguments passed. Controlling all doors."); + Console.Print("No arguments passed. Controlling all doors."); foreach (Sequencer door in _doors.Values) { if (door.Running) continue; @@ -70,12 +68,12 @@ namespace IngameScript string key = _cli.Argument(i); if (!_doors.ContainsKey(key)) { - _console.Print($"Door '{key}' not found. Skipping."); + Console.Print($"Door '{key}' not found. Skipping."); continue; } if (_doors[key].Running) { - _console.Print($"Door '{key}' already moving. Skipping."); + Console.Print($"Door '{key}' already moving. Skipping."); continue; } doorsToControl.Add(_doors[key]); @@ -83,11 +81,11 @@ namespace IngameScript if (doorsToControl.Count == 0) { - _console.Print("No doors found. Not creating new job."); + Console.Print("No doors found. Not creating new job."); } else { - _console.Print("Creating new job(s)."); + Console.Print("Creating new job(s)."); bool deploy = _cli.Switch("deploy") || _cli.Switch("open"); foreach (Sequencer door in doorsToControl) { @@ -105,7 +103,7 @@ namespace IngameScript _jobs[i].Dispose(); _jobs.Remove(_jobs[i]); i--; - _console.Print("Operation Complete."); + Console.Print("Operation Complete."); } if (_jobs.Count == 0) diff --git a/Mixins/Console/Console.cs b/Mixins/Console/Console.cs index 7c25e80..f6a0181 100644 --- a/Mixins/Console/Console.cs +++ b/Mixins/Console/Console.cs @@ -9,35 +9,48 @@ using VRage.Game.ModAPI.Ingame.Utilities; namespace IngameScript { + // A Program that supports consoles by initializing a MyIni instance + // and providing a public console for member objects to refer back to. + // (It is probably an anti-pattern that this lives outside of the Program class, + // but we can't find a cleaner way to implement this.) + public interface IConsoleProgram + { + MyIni Ini { get; } + Program.IConsole Console { get; } + } + partial class Program { public interface IConsole { void Print(string text); void PrintLower(string text); + void UpdateTickCount(); } - public class Console : IConsole + // You should only instantiate one MainConsole, typically in your Program code. + // Either use a reference to that instance directly where needed or use it to create PrefixedConsoles. + public class MainConsole : IConsole { - private MyGridProgram _program; + private Program _program; private int _maxLines; private List _buffer; + private StringBuilder _builder; + private string _programName; + private int _tickCount = 0; private const int DefaultMaxLines = 10; - public Console(MyGridProgram program, MyIni ini) + public MainConsole(Program program, string programName) { _program = program; + _programName = programName; _buffer = new List(); + _builder = new StringBuilder(); // Check the PB's custom data for a maxlines directive. - ini.TryParse(program.Me.CustomData); - _maxLines = ini.Get("console", "maxLines").ToInt32(DefaultMaxLines); - } - - public PrefixedConsole CreatePrefixedConsole(string prefix) - { - return new PrefixedConsole(this, prefix); + _program.Ini.TryParse(program.Me.CustomData); + _maxLines = _program.Ini.Get("console", "maxLines").ToInt32(DefaultMaxLines); } public void Print(string text) @@ -46,8 +59,16 @@ namespace IngameScript _program.Me.GetSurface(0).WriteText(writeToBuffer(text)); } - // Text written with this method goes to the lower screen / keyboard, - // with no buffering. + // Write the "standard" text to the lower console + public void UpdateTickCount() + { + PrintLower($"Airlock Controller\nTotal Ticks: {++_tickCount}"); + } + + // Most programs probably want to use UpdateTickCount to get program name and + // tick information. + // If you want something else on the lower screen, write it here. This is unbuffered, + // so text will be replaced instead of appended. public void PrintLower(string text) { _program.Me.GetSurface(1).WriteText(text); @@ -57,11 +78,11 @@ namespace IngameScript // string. private string writeToBuffer(string text) { + _builder.Clear(); _buffer.Add(text); if (_buffer.Count > _maxLines) _buffer.RemoveAt(0); - StringBuilder result = new StringBuilder("", 800); - foreach (string line in _buffer) result.AppendLine(line); - return result.ToString(); + foreach (string line in _buffer) _builder.AppendLine(line); + return _builder.ToString(); } } @@ -85,6 +106,7 @@ namespace IngameScript // sub-consoles can't print to the ephemeral display public void PrintLower(string text) { } + public void UpdateTickCount() { } } } } \ No newline at end of file