Open-Source Wikis

/

Godot

/

Modules

/

Mono / C#

godotengine/godot

Mono / C#

Purpose

modules/mono/ integrates the .NET runtime so projects can use C# (and any .NET language that compiles to .NET assemblies). Despite the directory name, modern Godot uses .NET 8 and the official hostfxr rather than the legacy Mono runtime; the module name has been retained for historical reasons.

Directory layout

modules/mono/
├── README.md                       Build instructions (read first)
├── config.py                       Build options + can_build (.NET SDK detection)
├── csharp_script.{cpp,h}           CSharpScript / CSharpInstance — Script subclass (~87 KB cpp)
├── csharp_script_resource_format.{cpp,h}  ResourceFormatLoader for .cs files
├── godotsharp_dirs.{cpp,h}         Per-platform asset paths
├── godotsharp_defs.h               Shared constants for native + managed
├── interop_types.h                 Layout of Godot's Variant equivalents on the managed side
├── managed_callable.{cpp,h}        Callable backed by a managed delegate
├── mono_gc_handle.{cpp,h}          Strong/weak GC handles for keeping managed refs alive
├── signal_awaiter_utils.{cpp,h}    Awaitable wrappers for engine signals
├── class_db_api_json.{cpp,h}       Dumps the C# binding API to JSON for tooling
├── mono_gd/                        Native helpers used during marshalling + interop
├── glue/                           C# glue: GodotSharp + GodotSharpEditor + tools (the managed side)
├── editor/                         Editor plugins (build dialog, project export, .NET solution sync)
├── thirdparty/                     Vendored bits used at build time (e.g., aotc helpers)
├── utils/                          Misc helpers
├── icons/, doc_classes/            Standard module assets
└── build_scripts/                  Python helpers invoked by SCons during the C# glue build

Two-pass build

Building the Mono module requires two passes:

  1. First pass — build Godot with module_mono_enabled=yes. This produces bin/godot.<platform>.editor.<arch> plus a small set of registration stubs.
  2. Generate glue./bin/godot --headless --generate-mono-glue modules/mono/glue runs the engine, walks ClassDB, and emits C# source under modules/mono/glue/GodotSharp/GodotSharp/Generated/ (one C# class per native class, with marshalling code).
  3. Build managed assembliesscons module_mono_enabled=yes build_assemblies=yes invokes dotnet build on the GodotSharp solution to produce GodotSharp.dll + GodotSharpEditor.dll + native interop assemblies under bin/GodotSharp/Api/.
  4. Second pass — re-run scons module_mono_enabled=yes to pick up the generated glue (the binary now references the C# bindings).

README.md in the module documents this loop in detail.

Runtime architecture

graph LR
    Editor[Godot editor / runtime] -->|hostfxr| Runtime[.NET 8 runtime]
    Runtime --> GodotSharp[GodotSharp.dll]
    GodotSharp --> User[User C# script: MyScript.cs]
    Runtime <-->|P/Invoke| Native[modules/mono native code]
    Native --> Object[Object / Variant / ClassDB]
    User -->|Calls / Properties| GodotSharp
    GodotSharp -->|gd_native_*| Native
    Native -->|dispatch| Object

hostfxr (from .NET) loads the runtime in-process. Once initialized:

  • GodotSharp.dll exposes types like Node, Sprite2D, Variant, GodotObject, Callable, Signal, Vector3. They are thin wrappers that P/Invoke into the native engine.
  • The native side calls into managed user scripts when scripts are attached to nodes. The handshake passes MonoGCHandle-tracked references so the .NET GC keeps script instances alive.
  • Variant is marshalled via interop_types.h — the layout is shared between native C++ and the C# Variant struct so memcpy-style transfer is safe.
  • Method calls go through generated marshalling stubs that pack arguments into Variants, call the engine method, and unpack the return value.

CSharpScript / CSharpInstance

csharp_script.cpp (~87 KB) is the C# Script integration:

  • CSharpScript is the Script subclass per .cs file. It loads the type, finds its base class, and prepares property/signal/method metadata for the editor.
  • CSharpInstance is the per-Object script instance. It holds the managed object, marshals property gets/sets, dispatches notifications, and calls signal handlers.
  • Hot reload is supported but with restrictions (changing class layout requires editor restart of the running game).

Editor integration

modules/mono/editor/ contributes:

  • Build dialog — runs dotnet build with the right project file; surfaces compiler errors in the editor's bottom panel.
  • Solution sync — the editor maintains a <project>.csproj and <project>.sln so users can open the project in Visual Studio, Rider, or VS Code.
  • Export plugin — the per-platform exporters know how to bundle .NET assemblies, AOT-compiled binaries (when applicable), and the .NET runtime.
  • GodotSharpEditor glue — implements EditorPlugin-style helpers in C# that the editor uses for generating bindings on the fly.

Signal / async integration

C# scripts can await engine signals because Signal exposes a GetAwaiter() extension that listens for the signal once and resumes the calling task:

await GetTree().CreateTimer(1.0).Timeout;

signal_awaiter_utils.cpp connects the once-only listener and wakes the awaiter on emission.

Per-platform AOT and packaging

  • Desktop targets ship the .NET runtime alongside the executable (resolved via hostfxr).
  • iOS requires AOT compilation; the export pipeline uses mono-aot-cross (or .NET 8 dotnet publish -p:PublishAot=true) to pre-compile assemblies.
  • Android packages assemblies in the APK and bundles a runtime archive.
  • WebAssembly via .NET 8 wasm support is partial / experimental in current versions.

godotsharp_dirs.cpp knows where on each platform to place runtime files.

Key abstractions

Abstraction File Role
CSharpScript / CSharpInstance modules/mono/csharp_script.cpp Script subclass + per-Object runtime state
MonoGCHandle modules/mono/mono_gc_handle.h RAII GC handle wrapper
ManagedCallable modules/mono/managed_callable.cpp Callable backed by a managed delegate
GodotSharp modules/mono/glue/GodotSharp/ (C#) Public C# API
GodotPlugins modules/mono/glue/ (C#) Native↔managed interop helpers

Integration points

  • register_types.cpp registers CSharpScript with ScriptServer and the language with the script editor.
  • The export plugin slots into editor/export/ so building+exporting C# projects is a one-click flow.
  • EngineDebugger integration lets the editor's debugger receive C# stack traces with file/line info from PDBs.

Entry points for modification

  • Updating the binding generator → modules/mono/glue/runtime_interop.{cpp,h} plus the SCons hooks in build_scripts/.
  • Changing how managed objects are tracked → mono_gc_handle.cpp + csharp_script.cpp::_GodotSharp_* callbacks.
  • Adding a new C# helper → put it in glue/GodotSharp/GodotSharp/Core/ and rebuild the solution.
  • Tweaking export packaging → modules/mono/editor/godotsharp_export.cpp (assembly bundling, AOT) and the per-platform exporters in platform/<os>/export/.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Mono / C# – Godot wiki | Factory