3. Hello, world! - momoadept/FleetCommand GitHub Wiki
This tutorial will guide you through outputting "Hello, world!" in Space Engineers using AdeptOS. It will get you started with some basic concepts before we'd need to go deeper. I assume that you went through Getting Started guide and have an empty project set up for your script, we'll go from there.
I recommend using "Trim unused types" and "Lite (no renaming)" by default. No renaming will help understand stack traces if something goes wrong, and the minification amount is good enough.
It's a good opportunity to see what AdeptOS does without any user code. Use MDK Deploy Script to build your script.
If you are getting errors at this stage, probably your project references are not configured correctly, see Getting Started guide
Get yourself a programmable block and plug your script in. After you're done, you should see AdeptOS performance output in the detailed info section of your PB. It should look something like this:
Also, the PB screen will start to display something.
Whenever you make changes to the script, re-deploy with MDK-SE and load the new version in the PB.
First, you need a module. It is the main building block of your code. Let's start by creating a new class using Utility Class template from the MDK-SE and name it HelloWorldModule
.
partial class Program
{
public class HelloWorldModule
{
}
}
Now we'll make it an AdeptOS module. We do it via implementing IModule
interface (HelloWorldModule : IModule
). At this point, and honestly every time you use AdeptOS types, I strongly recommend using your Visual Studio magic to generate not implemented members, since it is the best way to not miss anything. Let's do it and see what IModule
is:
public class HelloWorldModule : IModule
{
public string UniqueName { get; }
public string Alias { get; }
public void Bind(IBindingContext context)
{
}
public void Run()
{
}
public void OnSaving()
{
}
}
Now we are interested in Bind
and Run
methods, but I will go into more details on other stuff in later tutorials.
This is called by the framework once when the PB is loaded. It is the place where you do your Dependency Injection. If your module needs to use other modules, it will receive their instances here using the context
object. Let's pick up a few built-in modules that we will need for this tutorial (actually these two are a part of almost every module):
ILog _log;
IGameContext _gameContext;
public void Bind(IBindingContext context)
{
_log = context.RequireOne<ILog>();
_gameContext = context.RequireOne<IGameContext>();
}
Now, in our module class, we have _log
and _gameContext
properties, that are in fact other modules, and we can use them. Similarly, other modules can use our module, if we were to give it an interface, but for now we don't need to bother.
This is called by the framework once when the PB is loaded, but strictly after all the Bind()
calls. Here you can be sure that all your dependencies are in place and can start doing your stuff. Hello, world!
will go in here. This method parallels your public Program()
constructor for vanilla scripting as an entry point. In general, a module is very similar to a vanilla script, and where you were to use Program()
and Save()
, you now use Run()
and OnSaving()
. This way framework can run multiple modules without them knowing about each other. But there is a difference:
We don't have Tick()
. Every module doesn't neccessarily need one, and those that do, may need different update frequencies. We achieve this by using AdeptOS Jobs, and create them in Run()
method. We'll look into that in later tutorials. For now let's see how we can output something.
Before we do that, we need to let AdeptOS know that it should load our module. You do it by going to Program.cs
and adding our module instance to Modules = new List<IModule>()
definition:
Modules = new List<IModule>()
{
new BlackBoxLogger(),
new HelloWorldModule()
}
Now the framework will actually call methods on our module.
We'll go through two simplest options here. We will write in a log, and on an LCD panel.
Previously, we have obtained a ILog _log
object. AdeptOS has a built-in extensible log system. How would you debug your code otherwise? By default, the log goes to the PB main screen. Feel free to fiddle with font size on your own. Let's add some code to our Run()
:
public void Run()
{
_log.Info("Hello, world!");
}
That's it. Now run this in your PB and you will see the text on the screen.
Let's assume that you have built an LCD and named it [HelloWorld] Lcd whatever
. To write there we need to obtain IMyTextPanel
reference, after that it will go exactly as with vanilla scripting. But, since we are now in a module class, and not in Program
, we cannot use Me
and GridTerminalSystem
properties! For that we injected IGameContext _context
. It has Me
and Grid
for you to use.
There is a pre-built Tag
type, let's use it together with _gameContext
to discover our LCD. Add some code to Run()
so it looks like this:
public void Run()
{
_log.Info("Hello, world!");
var lcdTag = new Tag("HelloWorld"); // This will search for [HelloWorld]
var lcd = Tag
.FindBlockByTag<IMyTextPanel>(lcdTag, _gameContext.Grid)
.First();
}
Note that you can wave the .First()
call if you want to output to every LCD that has the tag, but you would need to use foreach
cycle to do that
Now lcd
is your LCD reference. Note that if no block is discovered, .First()
will throw, and the exception will be visible in the log. You can try this bad case on your own to get familiar to how the framework behaves when things go badly. For now let's use the LCD like this:
lcd.ContentType = ContentType.TEXT_AND_IMAGE;
lcd.WriteText("Hello, world!");
That's it. Now if you run it, and if your tag is set up correctly, you will see the text both on the PB screnn and on the LCD.
In the end, the code of your module should look like this:
partial class Program
{
public class HelloWorldModule : IModule
{
public string UniqueName { get; }
public string Alias { get; }
ILog _log;
IGameContext _gameContext;
public void Bind(IBindingContext context)
{
_log = context.RequireOne<ILog>();
_gameContext = context.RequireOne<IGameContext>();
}
public void Run()
{
_log.Info("Hello, world!");
var lcdTag = new Tag("HelloWorld"); // This will search for [HelloWorld]
var lcd = Tag
.FindBlockByTag<IMyTextPanel>(lcdTag, _gameContext.Grid)
.First();
lcd.ContentType = ContentType.TEXT_AND_IMAGE;
lcd.WriteText("Hello, world!");
}
public void OnSaving()
{
}
}
}
It is my expectation that after this tutorial you can use AdeptOS as if you were writing a vanilla script. Thousands of lines of code that sit there and do nothing, isn't that cool? Well, maybe you will at least find the Tag
class usefull. Please go on with the tutorials to see what AdeptOS brings to the table.