ic10extender 2.0.0

dotnet add package ic10extender --version 2.0.0
                    
NuGet\Install-Package ic10extender -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ic10extender" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ic10extender" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ic10extender" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ic10extender --version 2.0.0
                    
#r "nuget: ic10extender, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ic10extender@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ic10extender&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ic10extender&version=2.0.0
                    
Install as a Cake Tool

For Mod Developers

Setup

Non-Unity Mod

If you are creating a mod that does not add any prefabs or otherwise require unity, then your project setup should be pretty simple.

Open Visual Studio (2022), and create a new Class Library and ignore the Framework for now. (We will modify that in the next step)

Open your .csproj file and should have a section that looks something like this:

<PropertyGroup>
	<TargetFramework>net8.0</TargetFramework>
	<RootNamespace>IC10E_Direct_Reference_Extension</RootNamespace>
	<GameDir>C:\Program Files (x86)\Steam\steamapps\common\Stationeers</GameDir>
	<DebugType>embedded</DebugType>
</PropertyGroup>

Change TargetFramework to net4.8

Later in the file, there may be an <ItemGroup> section, where your dependencies are listed. If one doesn't exist, add the following:

<ItemGroup>
    <PackageReference Include="ic10extender" Version="1.0.1" />
</ItemGroup>

Otherwise, just insert the <PackageReference> line above. That will add this framework as a dependency from nuget.

Unity Mod

If you are creating a mod that does add prefabs, or otherwise requires unity, then your project setup will be a little less straightforward, since the .csproj file gets regularly regenerated.

In this case, you will need to get the IC10Extender.dll file. You can do so by subscribing to the mod on the workshop, navigating to its folder, and copying the dll.

You will need to paste this to your Assets/Assemblies/ folder.

Creating an OpCode

Adding an opcode has three parts.

  • The Operation abstract class represents the actual unit of work that is carried out when the opcode gets executed by the programmable chip.
  • The ExtendedOpCode abstract class is a factory class. It is used to retrieve information about the operation, and to acquire instances of the operation it represents.
  • Registering the opcode using IC10Extender.Register. This takes an instance of your sublcass of ExtendedOpCode.

Here is a simple example:

namespace MyNamespace
{
    public class MyOpCode : ExtendedOpCode
    {
        private static readonly HelpString[] Args = { /*An array of HelpStrings that describe what each argument is*/

        public ThrowOperation() : base(/*the actual opcode string*/) { }

        public override void Accept(int lineNumber, string[] source)
        {
            //This is your opportunity to throw a ProgrammableChipException to reject a line as not being correct
            //source includes the opcode itself, not just the args, so you need to take it into account when counting arguments
            if (source.Length != Args.Length + 1) throw new ProgrammableChipException(ICExceptionType.IncorrectArgumentCount, lineNumber);
        }

        // Returns an instance of the operation, using the text that was previously fed into the Accept() method
        public override Operation Create(ChipWrapper chip, int lineNumber, string[] source)
        {
            return new Instance(chip, lineNumber, source);
        }

        // This is used to create the help string that shows you what args are left while you are typing in the editor.
        // currentArgCount is mainly there to support varargs if your command needs it and does NOT include the opcode itself in the count.
        // Otherwise, it can be ignored.
        public override HelpString[] Params(int currentArgCount)
        {
            return Args;
        }

        // If you would like to change what color the opcode is highlighted as, you can also override the Color() method

        public class Instance : Operation
        {
            public Instance(ChipWrapper chip, int lineNumber, /*string arg1, string arg2, etc. or just string[] args*/*) : base(chip, lineNumber)
            {
                //set up your variables
            }

            public override int Execute(int index)
            {
                //your actual execution code
            }
        }
    }
}

You can of course, organize this however you want, but this is a nice, relatively self contained way of doing it.

Finally, in your mod entrypoint, just add IC10Extender.Register(new MyOpCode());

Creating a Preprocessor

A preprocessor is used to do manipulation of the source lines before it gets translated into Operation instances. Several preprocessors exist by default. They are executed in the order that they appear in the preprocessor list. An optional index has been provided so that mod authors can insert their preprocessor before another if necessary. The default processors, in order of registration/execution are:

  • CommentPreprocessor
  • StringPreprocessor (beta branch only at time of writing)
  • HashPreprocessor
  • BinaryLiteralPreprocessor
  • HexLiteralPreprocessor
  • LabelPreprocessor

Preprocessors are structured similarly to ExtendedOpCodes in that the Preprocessor class is your factory and will provide information around what its PreprocessorOperation instances do. Unlike ExtendedOpCode, however, they operate on all of the lines simultaneously, presented to them as a list of Line structs. The simplest implementation of a preprocessesor operation only needs to implement the Line? ProcessLine(Line line); method. This processes an individual line. By default if null is returned, then the line is deleted from the list, and will not be carried forward for further processing. More complicated preprocessor implementations can override IEnumerable<Line> DoPass(IEnumerable<Line> fullScript) or IEnumerable<Line> Process(IEnumerable<Line> fullScript) for more control as needed.

Opcode Lifecycle operations

This library now supports the ability to inject logic before or after the execution of an opcode. This can be done globally by adding a delegate to IC10Extender.PreExecute or IC10Extender.PostExecute

e.g.

public void LogExec(OpContext op, ref int index) {
    Logger.LogInfo($"Executed: {op.Raw}, jumped to {index} afterward");
}

// somewhere else in your code
IC10Extender.PostExecute+= LogExec;

Preprocessors can also do this for an indiviual line instead of globally, by adding to the Pre- and Post- Execute delegates for the individual line that they want to attach it to. The line's lifecycle delegates will be appened to the global lifecycle delegates upon creation of the LineOfCode instance.

Type Checking operations

In order to allow for lifecycle operations on vanilla commands (technically, any operation not registered with this library, not just vanilla), all operations will be wrapped in an OpContext constructed from the original operation and the Line associated with this LineOfCode. Strictly speaking, the OpContext itself is also wrapped by an OperationWrapper in order to be assignable to the ProgrammableChip._Operation class. So in general the object wrapping chain will look like one of the following:

# if the leaf operation is from this library
ProgrammableChip.LineOfCode -> OperationWrapper -> OpContext -> <author defined Operation subclass>

# otherwise (vanilla, or modded and directly subclassing ProgrammableChip._Operation)
ProgrammableChip.LineOfCode ->  OperationWrapper -> OpContext -> ReverseWrapper -> <vanilla or externally modded _Operation subclass>

Custom Constants

This library has a mechanism for registing custom constants, however I have not explicitly patched them into usage yet, so I am not sure this system is currently functional.

Using this as a soft dependency

This is a decent article on how to add a soft dependency

Additionally, IC10Inspector is a working example of this library being used as a soft dependency.

Understanding the various Operation.XXXXVariable classes

TBWritten as I am still working on fully understanding them myself. However, you can look at Direct Reference Extensions or crack open the game's Assembly-CSharp.dll file with a tool like dotPeek and poke around in Assets.Scripts.Objects.Electrical.ProgrammableChip for examples of them in use.

A future update is planned to include a fluent style builder to make the construction and usage of these variables more intuitive/understandable.

For Contibutors (or building locally)

This mod involves transpiling the ProgrammableChip class, most of whose internals are private. So in order to be able to access the internal classes, we have to first generate a publicized version of the dll. This dll will be used for linking, but it is NOT required to replace the game's version of the dll with this publicized version.

Installing the Publicizer

We will be using BepInEx.AssemblyPublicizer to generate our publicized dependency. I prefered to use the cli version.

Install the tool with dotnet tool install -g BepInEx.AssemblyPublicizer.Cli

Generating Publicized Dependency

In a terminal, navigate to the dependency we need to publicize. On Windows, this will be at: C:\Program Files (x86)\Steam\steamapps\common\Stationeers\rocketstation_Data\Managed

Run assembly-publicizer .\Assembly-CSharp.dll

This will create the file Assembly-CSharp-publicized.dll. Move this file into the publicized-dlls directory of this project.

Acknowledgements

Special thanks to tom_is_unlucky over on the stationeers modding discord for helping to debug the transpiler!

And of course, thanks to the other lovely people on that discord who have been helpful in getting this where it is.

Product Compatible and additional computed target framework versions.
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 268 8/30/2025
1.5.1 164 7/29/2025
1.5.0 158 7/28/2025
1.4.0 467 7/25/2025
1.3.0 459 7/21/2025
1.2.0 368 7/21/2025
1.0.1 118 7/19/2025
1.0.0 194 7/17/2025