Oxidizer
A mod loader for Kitten Space Agency, written in Rust. Mods can be written in Rust (cdylib) or as Rhai scripts. Runs on Windows and Linux.

KSA is a .NET 10 game, so the loader:
- loads the game's own
hostfxr(hostfxr.dll/libhostfxr.so) and starts its runtime withKSA.dll, - injects
bridge/(a small C# DLL plus Harmony), which hooksProgram.OnFrame,Program.OnDrawUiViewportsand the game's menu bar, and gives mods reflection access to game objects, - loads the mods, then runs the game's
Main.
In game, Mods > Oxidizer in the menu bar opens a window that lists the loaded mods, their errors and the log.
Install
- Download
Oxidizer-vX.Y.Z.zip(Windows) orOxidizer-vX.Y.Z-linux.zip(Linux) from Releases and unzip it anywhere. - Run
oxidizer.exe(Linux:./oxidizer). If KSA isn't installed at the default path, setgame_pathin theoxidizer.tomlit creates next to the exe. On Linux there is no default, so set it on the first run. - To try the examples, copy the folders from
example-mods/intoDocuments/My Games/Kitten Space Agency/mods/.
Build
Run
On first run without --game, the loader creates oxidizer.toml next to the exe (game_path = '...'). On Windows it defaults to C:\Program Files\Ahwoo\Games\Kitten Space Agency; on Linux, set it yourself.
-InstancePath, or the OXIDIZER_INSTANCE_PATH environment variable, makes the game use that folder instead of Documents/My Games/Kitten Space Agency for mods, saves and settings. Mod managers such as Borea use it for separate game profiles.
Mods
A mod is a folder in Documents/My Games/Kitten Space Agency/mods/<id>/ whose mod.toml has a [oxidizer] section:
= "my_mod"
[]
= "my_mod" # Rust mod: my_mod.dll on Windows, libmy_mod.so on Linux
= "main.rhai" # and/or Rhai script
# Tells mod managers such as Borea which loader this mod needs.
[]
= "Oxidizer"
= "0.2.0"
Mods set enabled = false in KSA's manifest.toml are skipped. See examples/ for working mods. To install one, copy its folder into mods/; for a Rust mod, also copy in the built library (.dll on Windows, lib*.so on Linux). A mod for both platforms can ship both files.
Rust
The crate is a cdylib that depends on oxidizer_api. Mod has init, frame, gui, menu (entries in the game's Mods menu) and unload; all but init are optional.
use ;
;
oxidizer_mod!;
Rhai
A script can define init(), frame(t, dt), gui(dt), menu() (entries in the game's Mods menu) and unload(). Keep state in this (an object map).
Scripts reload while the game runs: save the file and the new code takes over within a second. this is kept, init() doesn't run again, and reload() runs if the script defines it. If the new version doesn't compile, the old one keeps running and the error shows in the Oxidizer window.
| Area | Functions |
|---|---|
| Game objects | static_get(type, member), static_set(type, member, v), static_call(type, method[, args]), obj.get(m), obj.set(m, v), obj.call(m[, args]), obj.float(), obj.is_null(), obj.len(), obj[i], obj.to_string() |
| Patching | patch(type, method, "prefix"|"postfix", "fn_name"). The callback is fn_name(instance, args); returning false from a prefix skips the original method. |
| ImGui | begin(title) / end(), text, button, checkbox(label, v) -> v, slider(label, v, min, max) -> v, same_line, separator, header |
Menu (in menu()) |
menu_item(label[, checked]) -> clicked, begin_menu(label) / end_menu() for a submenu |
| Logging | print(...) |
Notes and limits
- Members are looked up by name through reflection. Type names can be assembly-qualified (
"KSA.Universe, KSA"), which works even before the game has loaded that type. callpicks the first overload with a matching argument count and converts arguments to the parameter types.- Patching uses the first overload of the method. Hooks receive
__instanceand__args, but not__result. - A Rhai script that throws an error stops until its file is saved again, so the same error isn't logged every frame. Errors from reflection calls are logged once each.
ref structmembers (for exampleReadOnlySpanproperties such asProgram.VehiclesInFrame) can't be read through reflection.