First stab (currently untested) for a dock loading/unloading script.

This commit is contained in:
Anna Rose 2025-02-23 02:07:55 -05:00
parent e739b4e64d
commit 3727cf8f20
11 changed files with 416 additions and 113 deletions

118
DockLoader/Dock.cs Normal file
View File

@ -0,0 +1,118 @@
using Sandbox.ModAPI.Ingame;
using SpaceEngineers.Game.ModAPI.Ingame;
using System.Collections.Generic;
using VRage.Game.ModAPI.Ingame.Utilities;
namespace IngameScript
{
partial class Program
{
// We don't really do anything with the modes other than Loading right now,
// but they are nice for consistency.
// TODO: we could have displays that print the current mode...
public enum DockMode
{
Loading,
Unloading,
Off,
}
public class Dock
{
public string Id { get; private set; }
public DockMode Mode { get; private set; } = DockMode.Off;
public bool Functional
{
get
{
return _ports.Count > 0 && _inputSorter != null && _outputSorter != null;
}
}
private IMyConveyorSorter _inputSorter;
private IMyConveyorSorter _outputSorter;
private List<IMyShipConnector> _ports = new List<IMyShipConnector>();
private bool _wasDocked = false; // Records whether a ship was docked last time we checked for one.
private IConsole _console;
public Dock(string id, IConsole console)
{
Id = id;
_console = new PrefixedConsole(console, id);
}
// Check and update the state of the Dock.
// In particular, this checks whether a ship has undocked since our last check,
// and if so it turns off the loading/unloading mechanism and releases the lock.
public void Update()
{
bool isDocked = false;
foreach (IMyShipConnector port in _ports)
{
if (port.IsConnected)
{
isDocked = true;
break;
}
}
if (_wasDocked && !isDocked) Reset();
_wasDocked = isDocked;
}
public void StartLoading()
{
Mode = DockMode.Loading;
_outputSorter.Enabled = false;
_inputSorter.Enabled = true;
}
public void StartUnloading()
{
Mode = DockMode.Unloading;
_inputSorter.Enabled = false;
_outputSorter.Enabled = true;
}
public void Reset()
{
Mode = DockMode.Off;
_inputSorter.Enabled = false;
_outputSorter.Enabled = false;
}
public void AddBlock(IMyTerminalBlock block, MyIni ini)
{
if (block is IMyConveyorSorter)
{
string direction = ini.Get("dockLoader", "direction").ToString();
switch (direction)
{
case "input":
_inputSorter = block as IMyConveyorSorter;
_inputSorter.Enabled = false;
break;
case "output":
_outputSorter = block as IMyConveyorSorter;
_outputSorter.Enabled = false;
break;
default:
_console.Print($"Invalid direction for '{block.CustomName}'");
break;
}
}
else if (block is IMyShipConnector)
{
_ports.Add(block as IMyShipConnector);
}
else
{
_console.Print($"Can't add block '{block.CustomName}'");
}
}
}
}
}

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netframework48</TargetFramework>
<RootNamespace>IngameScript</RootNamespace>
<LangVersion>6</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configurations>Release;Debug</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mal.Mdk2.PbAnalyzers" Version="2.1.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Mal.Mdk2.PbPackager" Version="2.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Mal.Mdk2.References" Version="2.2.4" />
</ItemGroup>
<ItemGroup>
<None Remove="Instructions.readme" />
<AdditionalFiles Include="Instructions.readme" />
<AdditionalFiles Include="thumb.png" />
</ItemGroup>
<Import Project="..\Mixins\Console\Console.projitems" Label="shared" />
</Project>

View File

@ -0,0 +1,22 @@
; This file is project specific and should be checked in to source control.
[mdk]
; This is a programmable block script project.
; You should not change this.
type=programmableblock
; Toggle trace (on|off) (verbose output)
trace=off
; What type of minification to use (none|trim|stripcomments|lite|full)
; none: No minification
; trim: Removes unused types (NOT members).
; stripcomments: trim + removes comments.
; lite: stripcomments + removes leading/trailing whitespace.
; full: lite + renames identifiers to shorter names.
minify=none
; A list of files and folder to ignore when creating the script.
; This is a comma separated list of glob patterns.
; See https://code.visualstudio.com/docs/editor/glob-patterns
ignores=obj/**/*,MDK/**/*,**/*.debug.cs

View File

@ -0,0 +1,7 @@
; This file is _local_ to your machine and should not be checked in to source control.
[mdk]
; Where to output the script to (auto|specific path)
output=auto
; Override the default binary path (auto|specific path)
binarypath=auto

View File

@ -0,0 +1,5 @@
R e a d m e
-----------
In this file you can include any instructions or other comments you want to have injected onto the
top of your final script. You can safely delete this file if you do not want any such comments.

124
DockLoader/Program.cs Normal file
View File

@ -0,0 +1,124 @@
using System.Collections.Generic;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
using System.Linq;
namespace IngameScript
{
public partial class Program : MyGridProgram, IConsoleProgram
{
public MyIni Ini { get; private set; }
public IConsole Console { get; private set; }
private MyCommandLine _cli = new MyCommandLine();
private Warehouse _warehouse;
private Dictionary<string, Dock> _docks = new Dictionary<string, Dock>();
public Program()
{
Ini = new MyIni();
Console = new MainConsole(this, "Dock Loader");
_warehouse = new Warehouse(Console);
// Find all the blocks with a [dockLoader] config
List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
GridTerminalSystem.GetBlocksOfType(blocks, blockFilter);
foreach (IMyTerminalBlock block in blocks)
{
Ini.TryParse(block.CustomData);
if (Ini.Get("dockLoader", "warehouse").ToBoolean(false)) _warehouse.AddBlock(block, Ini);
else
{
string id = Ini.Get("dockLoader", "id").ToString();
if (id == "")
{
Console.Print($"Ignoring invalid block '{block.CustomName}'");
continue;
}
if (!_docks.ContainsKey(id)) _docks[id] = new Dock(id, Console);
_docks[id].AddBlock(block, Ini);
}
}
// Remove any non-functional docks.
foreach (Dock dock in _docks.Values.ToList())
{
if (!dock.Functional)
{
Console.Print($"Dock '{dock.Id}' not fully configured, ignoring.");
_docks.Remove(dock.Id);
}
}
Console.Print($"Found {_docks.Count} docks to manage.");
Runtime.UpdateFrequency |= UpdateFrequency.Update100;
}
public void Main(string argument, UpdateType updateSource)
{
handleInput(argument);
// Check on loading state
int loadingCount = 0;
bool timeout = _warehouse.Timeout;
foreach (Dock dock in _docks.Values)
{
dock.Update();
if (dock.Mode == DockMode.Loading)
{
if (timeout) dock.Reset();
else loadingCount++;
}
}
// This will decrement any loading timer, and switch to unloading mode when we do timeout.
// It will also switch to unloading mode if we pass it a 0.
if (updateSource.HasFlag(UpdateType.Update100)) _warehouse.Update(loadingCount);
}
private bool blockFilter(IMyTerminalBlock block)
{
return block.IsSameConstructAs(this.Me) && MyIni.HasSection(block.CustomData, "dockLoader");
}
private void handleInput(string argument)
{
if (argument == "") return;
_cli.TryParse(argument);
if (_cli.ArgumentCount != 2)
{
Console.Print("Must call script with exactly 2 arguments.");
return;
}
string command = _cli.Argument(0).Trim().ToLower();
string dockId = _cli.Argument(1).Trim().ToLower();
if (!_docks.ContainsKey(dockId))
{
Console.Print($"Unknown dock '{dockId}'");
return;
}
switch (command)
{
case "load":
_docks[dockId].StartLoading();
_warehouse.StartLoading();
break;
case "unload":
_docks[dockId].StartUnloading();
break;
case "reset":
_docks[dockId].Reset();
break;
default:
Console.Print($"Unknown command '{command}'");
break;
}
}
}
}

72
DockLoader/Warehouse.cs Normal file
View File

@ -0,0 +1,72 @@
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
namespace IngameScript
{
partial class Program
{
public class Warehouse
{
public bool Timeout
{
get
{
return _timeoutTicks == 0;
}
}
private IMyConveyorSorter _inputSorter;
private IMyConveyorSorter _outputSorter;
private IConsole _console;
private uint _timeoutTicks = 0;
public Warehouse(IConsole console)
{
_console = new PrefixedConsole(console, "warehouse");
}
public void Update(int loadingCount)
{
if (_timeoutTicks > 0)
{
_timeoutTicks--;
if (_timeoutTicks == 0 || loadingCount == 0)
{
_timeoutTicks = 0;
_outputSorter.Enabled = false;
_inputSorter.Enabled = true;
}
}
}
// A dock has requested loading
public void StartLoading()
{
_inputSorter.Enabled = false;
_outputSorter.Enabled = true;
_timeoutTicks = 75; // This is approximately 2 minutes.
}
public void AddBlock(IMyTerminalBlock block, MyIni ini)
{
if (!(block is IMyConveyorSorter))
{
_console.Print($"Ignoring incompatible block '{block.CustomName}'");
return;
}
switch (ini.Get("dockLoader", "direction").ToString())
{
case "input":
_inputSorter = block as IMyConveyorSorter;
break;
case "output":
_outputSorter = block as IMyConveyorSorter;
break;
default:
_console.Print($"Invalid direction for '{block.CustomName}'");
break;
}
}
}
}
}

BIN
DockLoader/thumb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

35
ScriptTemplate.cs Normal file
View File

@ -0,0 +1,35 @@
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
namespace IngameScript
{
public partial class Program : MyGridProgram, IConsoleProgram
{
public MyIni Ini { get; private set; }
public IConsole Console { get; private set; }
private MyCommandLine _cli = new MyCommandLine();
public Program()
{
Ini = new MyIni();
Console = new MainConsole(this, "Program Name");
}
public void Main(string argument, UpdateType updateSource)
{
handleInput(argument);
}
private bool blockFilter(IMyTerminalBlock block)
{
return block.IsSameConstructAs(this.Me);
}
private void handleInput(string argument)
{
if (argument == "") return;
_cli.TryParse(argument);
}
}
}

View File

@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DockingController", "Dockin
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoDisplays", "InfoDisplays\InfoDisplays.csproj", "{4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoDisplays", "InfoDisplays\InfoDisplays.csproj", "{4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DockLoader", "DockLoader\DockLoader.csproj", "{A4D9F936-93B2-423A-BD76-A97A084BD605}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -43,6 +45,10 @@ Global
{4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Debug|Any CPU.Build.0 = Debug|x64 {4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Debug|Any CPU.Build.0 = Debug|x64
{4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Release|Any CPU.ActiveCfg = Release|x64 {4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Release|Any CPU.ActiveCfg = Release|x64
{4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Release|Any CPU.Build.0 = Release|x64 {4C7C8B10-1E43-453E-A9DA-A3626FE85ACC}.Release|Any CPU.Build.0 = Release|x64
{A4D9F936-93B2-423A-BD76-A97A084BD605}.Debug|Any CPU.ActiveCfg = Debug|x64
{A4D9F936-93B2-423A-BD76-A97A084BD605}.Debug|Any CPU.Build.0 = Debug|x64
{A4D9F936-93B2-423A-BD76-A97A084BD605}.Release|Any CPU.ActiveCfg = Release|x64
{A4D9F936-93B2-423A-BD76-A97A084BD605}.Release|Any CPU.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,113 +0,0 @@
// Docking controller script; handles docking and undocking.
// Specifically, when you activate the script it will:
// * Connect any Connectors whose names start with "Docking Port" that are ready to connect.
// * Turn off all engines.
// * Set all O2 and Hydrogen tanks to Stockpile.
//
// Recharging batteries needs to be done manually, since the script can't finish with the
// batteries disabled.
//
// When you activate the script with "-undock" it will reverse all of these actions.
//
// TODO: None of the below switches work yet.
// You can selectively disable some functionality with switches.
// * "-no-refuel" to disable refueling
MyCommandLine cli;
IEnumerator<bool> state;
List<IMyShipConnector> dockingPorts;
List<IMyThrust> thrusters;
List<IMyGasTank> tanks;
public Program() {
cli = new MyCommandLine();
state = null;
dockingPorts = new List<IMyShipConnector>();
List<IMyShipConnector> allConnectors = new List<IMyShipConnector>();
GridTerminalSystem.GetBlocksOfType(allConnectors);
foreach(IMyShipConnector connector in allConnectors) {
if (connector.CustomName.StartsWith("Docking Port")) {
dockingPorts.Add(connector);
}
}
thrusters = new List<IMyThrust>();
GridTerminalSystem.GetBlocksOfType(thrusters);
tanks = new List<IMyGasTank>();
GridTerminalSystem.GetBlocksOfType(tanks);
Echo($"Found {dockingPorts.Count} docking ports.");
Echo($"Found {thrusters.Count} thrusters");
Echo($"Found {tanks.Count} tanks");
}
public void Main(string argument, UpdateType updateSource) {
if (state == null) {
cli.TryParse(argument);
if (cli.Switch("undock")) state = Undock();
else state = Dock();
Runtime.UpdateFrequency = UpdateFrequency.Update100;
return;
}
if (!state.MoveNext()) {
Runtime.UpdateFrequency = UpdateFrequency.None;
state.Dispose();
state = null;
}
}
private IEnumerator<bool> Dock() {
Echo("Initiating docking operation.");
foreach (IMyShipConnector dockingPort in dockingPorts) {
if (dockingPort.Status == MyShipConnectorStatus.Connectable) {
dockingPort.Connect();
}
}
bool docked = false;
while (!docked) {
yield return true;
foreach (IMyShipConnector dockingPort in dockingPorts) {
if (dockingPort.Status == MyShipConnectorStatus.Connected) {
docked = true;
break;
}
}
}
Echo("Docking clamp engaged. Shutting down systems.");
foreach (IMyThrust thruster in thrusters) {
thruster.Enabled = false;
}
foreach (IMyGasTank tank in tanks) {
tank.Stockpile = true;
}
yield return false;
}
private IEnumerator<bool> Undock() {
Echo("Initiating undocking operation.");
foreach (IMyGasTank tank in tanks) {
tank.Stockpile = false;
}
foreach (IMyThrust thruster in thrusters) {
thruster.Enabled = true;
}
foreach (IMyShipConnector dockingPort in dockingPorts) {
if (dockingPort.Status == MyShipConnectorStatus.Connected) {
dockingPort.Disconnect();
}
}
yield return false;
}