Programmable Blocks are an in-game way that lets players execute custom scripts that can interact with any other block in the game.
You write scripts using the C# language and the SE API (Application Programmable Interface) which can perform any functionality or access any data normally retrieved through the control panel of a block.
This page will act as a basic guide on writing scripts and outline how to interact with various blocks. It is not intended as an introduction to C#/programming principles - if you have never written code before, please look up basic guides elsewhere.
Contents
- 1 Aside: Scripts from the Steam Workshop
- 2 Requirements
- 3 Script Layout
- 4 Looking up Documentation
- 5 Block Types
- 6 GridTerminalSystem
- 6.1 Fetching a block by name
- 6.2 Fetching all blocks of a type
- 6.3 Fetching blocks by group
- 6.4 Searching for blocks by name
- 7 Block Actions
- 8 Using LCD Screens
- 8.1 Using an LCD Block
- 8.2 Using Multi-Screen Blocks
- 8.3 Displaying Text
- 8.4 Using Both Types In The Same Variable
- 8.5 More Actions
- 9 Accessing Inventories
Aside: Scripts from the Steam Workshop[]
This guide is about writing your own scripts, but be aware that PC users can alternatively also subscribe to scripts in the Steam Workshop, and load them into the programmable block.
When searching the Workshop, select the filter “Type: IngameScript” (not “Mod category: Script”)!
Requirements[]
Space Engineers runs scripts only if Experimental mode is enabled. And each saved game runs scripts only if its World Settings allow in-game scripts. On Xbox, Scripts are disallowed by Microsoft.
It’s very helpful to install and set up an Integrated Development Environment (IDE) for C#, specifically, many SE developers use [Visual Studio]. Point it to the SE API doc to get in-editor help.
Script Layout[]
In game, build a Programmable Block. On first opening the code editor inside it, you will see 3 empty methods already in place:
- Program() - Serves as a constructor to intialise objects. Executed once at the start, after the script has been compiled.
- Save() - Executed once at the end of a session, useful in case you need the script to save its state.
- Main() - Runs every time the script is executed by the player. This is the only method that is mandatory for the script to run.
Main() will hold most of the functionality for the script.
Looking up Documentation[]
Bookmark the Quick Introduction to Space Engineers Ingame Scripts.
Many blocks have been renamed over time, and their C# objects still have the old names. An IDE has features that help you orient yourself in the API: For example, if you are looking for "o2/h2 generator", typing IMyOxygen… in the IDE’s editor shows IMyOxygenGenerator, and it then reminds you that object is obsolete and to use IMyGasGenerator.
You'll also get helpful responses from the #programmable-block channel in Keen’s discord server.
Block Types[]
To interact with a block, it has to be read into a local variable as an object. It is considered good practice to assign blocks to variables only in the Program() method.
All blocks are subclasses of the IMyTerminalBlock class. There are a number of classes used to represent various kinds of blocks, with different public methods and properties.
For example, a Refinery is represented by the IMyRefinery object which includes specialised functions like IsProducing() and NextItemInQueue(), but also inherits generic functions and properties like 'CustomName' from IMyTerminalBlock.
The main block classes worth mentioning are:
- IMyTerminalBlock - As already mentioned, forms the base class from which all other block definitions inherit, either directly or through multiple layers of the hierarchy. Mostly includes generic functionality for checking ownership, naming, show on HUD etc.
- IMyProductionBlock - This is the class definition for all blocks which produce items (e.g. Refinery, Assembler, Arc Furnace). It defines useful interfaces for these blocks, including:
- IsProducing - Boolean showing whether the block is currently processing something or not.
- IsQueueEmpty - Boolean showing whether items are currently in the queue
- IMyTextSurface - Definition for all LCD screens. It includes functionality to format and display text or icons on a screen, such as:
- WriteText(string value, bool append=false) - This function sets the text for a screen to the string specified by the value parameter. The optional 'append' parameter can be set to true to append the string to the end of the current content rather than overwriting. Text displayed this way will be visible by anyone on the server.
- Don't forget to look at the section "Using LCD screens" for more info!
GridTerminalSystem[]
All interaction with in-game blocks has to take place through the GridTerminalSystem interface. It provides access to all blocks connected to the same in-game grid as the programmable block being used.
In order to start using a block, a local reference to the object must be intialised from the GridTerminalSystem. For example, to intialise an LCD Panel with the name 'LCD Panel 1':
IMyTextPanel panel = (IMyTextPanel)GridTerminalSystem.GetBlockWithName("LCD Panel 1");
Note that the value returned by GridTerminalSystem has to be cast into the relevant type (including IMyTerminalBlock).
Fetching a block by name[]
IMyTerminalBlock block = (IMyTerminalBlock)GridTerminalSystem.GetBlockWithName("Example Name");
Fetching all blocks of a type[]
List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();GridTerminalSystem.GetBlocksOfType<IMyRefinery>(blocks);
- Populates the 'blocks' list with all blocks of the specified type (e.g. all Refineries) so you can loop over them.
Fetching blocks by group[]
List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();GridTerminalSystem.GetBlockGroupWithName("Example Group").GetBlocksOfType<IMyTerminalBlock>(blocks);
- Populates the 'blocks' list with all blocks which are part of the specified group. Note that 'GetBlockGroupWithName()' returns an object representing the group rather than the individual blocks - using a GetBlocksOfType<IMyTerminalBlock> next fetches all blocks from this group.
Searching for blocks by name[]
List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();GridTerminalSystem.SearchBlocksOfName("Refinery", blocks);
- Populates the 'blocks' list with all blocks which contain the specified string in their name. For example this search here would match blocks of name "Refinery", "Station Refinery 1", etc.
Block Actions[]
This section is a work in progress
For now, refer to https://spaceengineerswiki.com/Programming_Guide/Action_List and https://github.com/malware-dev/MDK-SE/wiki to find most actions.
Using LCD Screens[]
There are 2 types of LCD screens.
IMyTextSurface
are normal LCD blocks)IMyTextSurfaceProvider
is any block with multiple LCDs built in, like a co*ckpit or a programmable block.
Using an LCD Block[]
IMyTextSurface textSurface;
LCDscreen = GridTerminalSystem.GetBlockWithName("LCD");
textSurface = (IMyTextSurface)LCDscreen;
- This will give you a variable (
textSurface
) that you can use to display text on usingtextSurface.WriteText("Hello world!", false);
Using Multi-Screen Blocks[]
IMyTextSurface textSurface;
LCDscreen = GridTerminalSystem.GetBlockWithName("LCD");
textSurface = ((IMyTextSurfaceProvider)LCDscreen).GetSurface(int screenNumber);
- This will give you a variable (
textSurface
) that you can use to display text just like a normal LCD screen.
Unlike the normal LCD, an IMyTextSurfaceProvider
requires a number to indicate which screen to use. In the block terminal, you can see a list of screens. (For example, a programmable block has 2: Large Display and Keyboard.) The topmost screen is 0, and the second screen is 1. In many co*ckpits, there are more LCDs, but the list always starts at 0.
Displaying Text[]
The method WriteText();
accepts 2 arguments, string Text
and bool Append
.
- The string is the text the screen will display. Don't forget to use the
\n
character to start new lines, as the string will otherwise always continue on the same line, often making text go off screen. - The Append option lets you choose if the entire screen gets cleared first (
Append = false
), or your text will be added after the current text (Append = true
).
Usage example: textSurface.WriteText("Hello world!", false);
Using Both Types In The Same Variable[]
In order to use the 2 types of screens in the same variable, we will need to check which type of screen it is. The following code is an example of how to tackle this problem.
IMyTextSurface textSurface;
LCDscreen = GridTerminalSystem.GetBlockWithName("LCD");
int screenNumber = 0;
if (LCDscreen is IMyTextSurface) //If the screen is an LCD block
{
textSurface = (IMyTextSurface)LCDscreen;
}
else if (LCDscreen is IMyTextSurfaceProvider) //If the screen is a co*ckpit / programmable block
{
textSurface = ((IMyTextSurfaceProvider)LCDscreen).GetSurface(screenNumber);
}
else //If the screen is not set, use the current programmable block
{
textSurface = ((IMyTextSurfaceProvider)Me).GetSurface(screenNumber);
}
After this code, you can write anything to the screen using textSurface.WriteText();
Despite the different screen types.
More Actions[]
You can find more actions at Malware's Development Kit for Space Engineers (Search IMyTextSurface and IMyTextSurfaceProvider)
Accessing Inventories[]
This section is a work in progress