space_engineers/Mixins/Sequencer/Sequencer.cs

74 lines
2.9 KiB
C#

// Represents a single set of blocks to run in order, waiting for each group's completion before
// moving on to the next one.
using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;
using System.Linq;
namespace IngameScript
{
partial class Program
{
public class Sequencer
{
public bool Running { get; private set; }
public string Name { get; private set; }
private IConsole _console;
private SortedDictionary<int, List<ISequenceable>> _sequence = new SortedDictionary<int, List<ISequenceable>>();
public Sequencer(Program program, string name)
{
Name = name;
_console = new PrefixedConsole(program.Console, Name);
}
public void AddBlock(ISequenceable block)
{
if (!_sequence.ContainsKey(block.Step)) _sequence[block.Step] = new List<ISequenceable>();
_sequence[block.Step].Add(block);
}
// To activate the Sequencer, call this once, then call `MoveNext()` once per tick
// on the returned object until it returns false.
// (then be sure to call `Dispose() on that object`)
public IEnumerator<bool> RunSequence(bool deploy = true)
{
if (Running)
{
_console.Print("Already running, ignoring invocation.");
yield break;
}
// This is a bit convoluted, but we're extracting the iterator for use in the foreach directly.
// This allows us to iterate in reverse when reversing/"closing" the sequence.
IEnumerable<KeyValuePair<int, List<ISequenceable>>> steps = deploy ? _sequence : _sequence.Reverse();
foreach (KeyValuePair<int, List<ISequenceable>> kvp in steps)
{
List<ISequenceable> blocks = kvp.Value;
List<IEnumerator<bool>> subJobs = new List<IEnumerator<bool>>();
Running = true;
foreach (ISequenceable block in blocks)
{
// TODO: add some sort of handling for block.Running == true, maybe?
// It *should* be an impossible case, but...
subJobs.Add(block.Run(deploy));
}
while (Running)
{
Running = false;
foreach (IEnumerator<bool> subJob in subJobs)
{
if (subJob.MoveNext()) Running = true;
}
yield return true;
}
foreach (IEnumerator<bool> subJob in subJobs) subJob.Dispose(); // clean up after ourselves
}
}
}
}
}