--- page: Getting Started section: Start Here source: https://2dog.dev/getting-started.md description: Run Godot inside .NET applications with desktop, web, and test hosts. --- # Let's take Godot for a walk! **2dog** embeds **Godot** inside **.NET** applications to help build games, apps, and tools for **desktop**, **web**, and **mobile**. It simplifies build automation, has templates for integration with **UI** frameworks and **web servers**, and running automated **tests** in your IDE. ![a white anthro dog in a hacker hoodie and glasses walking their blue godot robot dog](img/2dog-walkies.webp) ## Who's a good robot? Things you can do with 2dog may be complex, so we try to make it developer friendly. ::: tip From stock Godot to the browser With the [.NET 10 SDK](https://dotnet.microsoft.com/download) installed, in the base directory of your Godot project (e.g. `~/MyGame`), run: ```bash dnx 2dog add --web dotnet publish MyGame.web ``` Your app/game and `index.html` are then in `MyGame.web/AppBundle/` - easy! Upload that to itch.io, your web space, or serve locally, for example: `dnx dotnet-serve -d MyGame.web/AppBundle` or `npx serve MyGame.web/AppBundle` *(use your project's name, i.e. the one in `project.godot`, in place of `MyGame`)* ::: ## Developing Just use Godot or your favorite IDE. Building with 2dog uses [MSBuild](https://learn.microsoft.com/en-us/visualstudio/msbuild/walkthrough-using-msbuild), which runs on Windows x64, Linux x64, or macOS ARM64. 2dog upgrades your `.sln` file to the newer `.slnx` (after making a backup `.sln.old`), which you can open in your IDE or editor of choice. Godot understands both solution formats. The hosts you add appear as projects inside the solution, where you can extend and debug them as normal .NET projects. Use your IDE or `dotnet` CLI to build/run/debug. ## Editing & Importing Keep editing scenes and scripts in Godot as usual! Continue using the official [Godot 4.7.2 .NET editor](https://godotengine.org/download) for scene editing, development, and use `Project→Export...` to make classic Godot builds using the familiar export templates. 2dog's Hosts import changed resources automatically, you won't need `godot --import`. (you may still run it, of course) See [Hosts](/hosts/) for more .NET app types, and [Testing with xUnit](/testing) for tests... *(it's awesome!)* ## Updating & Fixing The project's 2dog package dependencies stay pinned until you update them. Run the latest tool to update or check for problems: ```bash dnx 2dog update dnx 2dog doctor ``` ## Squirrel ?! 2dog can do a lot more, such as provide unit testing, embedding in other applications and UIs, etc. It's maybe safest to try this in a completely fresh project, and *you know you wanted to start a new game project anyway!* To create a project from scratch, `project.godot` and all, from the command line: ```bash dnx 2dog new NewGame cd NewGame dotnet run --project NewGame.2dog dotnet test godot-mono -e . # or open it in the Godot project picker, etc. ``` --- page: Core Concepts section: Start Here source: https://2dog.dev/concepts.md description: How 2dog inverts Godot ownership: your .NET host owns the process and drives embedded libgodot, with GodotSharp API access, the main loop, resource paths, and the single-instance rule. --- # Core Concepts Teaching an old robot new tricks! *(and it won't forget any of the older ones)* 2dog works by adding small "sidecar" projects called [Hosts](/hosts/) to your solution that do the embedding and offer you to run your own code before, on top of, or after Godot. It uses `libgodot`, which became an official part of Godot in version 4.6, to achieve this. #### A companion, not a replacement Your original Godot project stays and works the same. Simply continue developing it using the official Godot editor if you like. ## 2dog is Godot, backward! But the side car projects allow us to do things quite differently! Traditional Godot applications have Godot control the process lifecycle: ``` Godot Process → GodotSharp (.NET) → SceneTree → Scripts ``` 2dog rolls over: ``` .NET Process → twodog.Engine → Godot (as libgodot) → GodotSharp → SceneTree → Scripts ``` Your .NET process controls startup, frames, and shutdown. Godot becomes a rendering, physics, and audio library that your application drives. ## `libgodot` ... ?! 2dog uses `libgodot`, a shared-library build of Godot Engine. The library is loaded by a .NET process, supports direct P/Invoke calls to native APIs, and retains full access to GodotSharp managed bindings. It compiles and runs on most platforms. #### Wait, co Godot could just *do that all this time?* Almost, but... yay for free and libre open source! 2dog is technically a 'fork' or a 'distro' of Godot, making some limited changes to the engine to allow it to tear down and start up more cleanly, to be not the first or primary loader of .NET assemblies, and more. It provides these as binaries for developers in easy to install `.nuget` packages. The [2dog tool](./dnx-2dog) grabs all these for us, check its output or do a `--dry-run`. For example, the native library (`libgodot.dll`, `libgodot.so`, or `libgodot.dylib`) ships in the `2dog.win-x64`, `2dog.linux-x64`, or `2dog.osx-arm64` NuGet packages, respectively. `2dog.engine` references the appropriate packages automatically. Each platform package ships three native variants of `libgodot`: `debug` (assertions and error checking), `release` (optimized for production), and `editor` (`TOOLS_ENABLED`, editor APIs, resource import). `TwoDogVariant` follows the Debug, Release, and Editor .NET configurations by default; [Build Variants](./build-configurations) is the complete guide. ## GodotSharp ... ! That's the current official C# API for Godot. Your game needs it, and 2dog has us covered. After startup, the full GodotSharp API is accessible: ```csharp using var engine = new Engine("MyGame", args: args); GodotInstance godot = engine.Start(); // Borrowed compatibility handle. // Access the scene tree SceneTree tree = engine.Tree; // Load and instantiate scenes var scene = GD.Load("res://my_scene.tscn"); var instance = scene.Instantiate(); tree.Root.AddChild(instance); // Use any Godot API var viewport = tree.Root.GetViewport(); var physics = PhysicsServer3D.Singleton; ``` ## Wow, I have a Main Loop now? Unlike traditional Godot, the host explicitly pumps Godot in its own main loop. It comes with a basic implementation that mimicks stock Godot, and you can extend it in each sidecar host project individually. ```csharp while (!engine.Iteration()) { // Godot processes physics, rendering, input, and your frame logic here. if (someCondition) break; // Exit when you decide } ``` `Engine` owns the running instance. `Iteration()` returns `true` when Godot wants to quit, such as when the window closes; `RequestQuit()` asks it to stop. After teardown, `Completion` completes and `Exited` fires on every platform. Desktop disposal is synchronous; browser disposal is asynchronous. ## Still Single Instance only... (for now) The way Godot is written, unfortunately we can't run multiple Godot instances in one Assembly Load Context. 2dog builds and expands on recent changes to the engine's init and teardown code, and you can now sequentially start new instances. Just wait for completion and create a new `twodog.Engine`. ### ... didn't mean you can't run multipe Godot engines *per process.* If you're a goated coder and know the ins and outs of juggling Assembly Load Contexts, read deeper into [Single Godot Instance](./known-issues/single-instance) for examples and an experimental isolated-hosting path to kind of get multiple engines after all. --- page: Project Layout section: Start Here source: https://2dog.dev/project-layout.md description: The recommended 2dog layout: the directory containing project.godot is the .NET solution root, with small nested host projects for desktop, browser, and tests that Godot ignores. --- # Project Layout 2dog recommends using the directory containing `project.godot` as the .NET solution root, with a small nested project for each host. Godot keeps its familiar layout while desktop, tests, and the browser get independent entry points. ## The Layout at a Glance ```text MyGame/ Godot project and solution root ├── project.godot Godot settings and main-scene selection ├── MyGame.csproj Godot C# game assembly ├── MyGame.slnx One solution for the complete project ├── scenes/ Scenes and resources ├── scripts/ C# game scripts ├── MyGame.2dog/ │ ├── .gdignore │ ├── MyGame.2dog.csproj │ └── Program.cs Desktop entry point ├── MyGame.tests/ │ ├── .gdignore │ ├── MyGame.tests.csproj │ └── BasicTests.cs Headless xUnit tests └── MyGame.web/ ├── .gdignore ├── MyGame.web.csproj └── wwwroot/ Browser shell and static files ``` Organize scenes and scripts however you prefer. Keep game content beside `project.godot` and executable hosts in ignored subdirectories. ## One Game Assembly `MyGame.csproj` remains the Godot C# project. It owns: - C# game scripts, generated source, and game-assembly configuration - The assembly Godot loads for your game - The `Godot.NET.Sdk` configuration - Browser bootstrap code shared with the web host Scenes, resources, and assets remain beside `project.godot`. Hosts reference the game assembly and content; they do not duplicate either. ## Three Ways to Run It ### 2dog (generic .NET) `MyGame.2dog` starts the embedded engine, points it at the parent Godot project, and drives the main loop. ```bash dotnet run --project MyGame.2dog ``` ### Test Host `MyGame.tests` starts Godot through an xUnit fixture, normally headless, and loads the real game assembly and resources. ```bash dotnet test MyGame.tests ``` ### Web Host `MyGame.web` is a .NET WebAssembly host. Publishing it builds the managed application, imports and exports the Godot content, and assembles a static site. ```bash dotnet publish MyGame.web ``` ## Why the Hosts Are Nested Nesting lets hosts find the Godot project. `.gdignore` prevents the editor from mistaking host source files for game scripts. Every host points its `GodotProjectDir` property at the parent directory: ```xml .. ``` The path is embedded as assembly metadata, used at runtime, and enables automatic resource import during builds. See [Resource Import](/import-tool). The game project excludes host folders from its default .NET compile globs, so the layers do not consume each other's source files. ## What the Godot Editor Sees Open `MyGame/` in Godot as usual: ```bash godot-mono --editor MyGame ``` The hosts' `.gdignore` files hide them from the editor, importer, and exporter. ## How You Get This Layout For an existing project, conversion adds the hosts around your current files: ```bash cd path/to/MyGame && dnx 2dog add ``` For a fresh project, the same tool creates the game and hosts together: ```bash dnx 2dog new MyGame ``` See [`2dog add`](/cli/add) or [`2dog new`](/cli/new) to get started, and the [CLI reference](/dnx-2dog) for all options. ## When to Use Another Layout An existing .NET application can host a Godot project elsewhere. Prefer the standard layout unless an established repository or application boundary gives you a concrete reason not to. For one minimal alternative, see the [Parallel xUnit Collections Demo](https://github.com/outfox/2dog/tree/main/demos/xunit/parallel_collections), which uses a small Godot project stub beside MSBuild projects with native dependencies. --- page: What's a Host? section: Hosts source: https://2dog.dev/hosts/index.md description: Choose a .NET application that starts Godot for your game or tests. --- # Hosts A host is a .NET application that starts Godot for your game or its tests. Each host has its own entry point and references the same game project. You can have multiple hosts of the same type, each will have its unique project. ## Add a Host Run this from the directory containing `project.godot`: ```bash dnx 2dog add ``` Choose hosts when prompted, or use the flags below. For a new project, use `dnx 2dog new MyGame` instead. | Host | Flag | Purpose | | --- | --- | --- | | [2dog (generic .NET)](./generic) | `--generic` | .NET console application (desktop or headless) | | [Android](./android) | `--android` | Android APK (experimental) | | [Avalonia](./avalonia) | `--avalonia` | Cross-platform desktop UI | | [Blazor](./blazor) | `--blazor` | Game inside a Blazor page | | [Web](./web) | `--web` | WASM / HTML5 browser bundle (e.g. for [itch.io](https://itch.io))| | [WebXR](./webxr) | `--webxr` | Browser VR and AR | | [WinForms](./winforms) | `--winforms` | Windows Forms UI | | [WinUI 3](./winui) | `--winui` | Windows App SDK UI | | [xUnit](./xunit) | `--tests` | Tests using the Godot engine | Without host flags, unattended commands include generic .NET, Web, and xUnit. See [Project Layout](/project-layout) for files and [MSBuild Configuration](/configuration) for shared settings. --- page: 2dog (generic .NET) section: Hosts source: https://2dog.dev/hosts/generic.md description: Run and publish your Godot game as a .NET console application. --- # 2dog (generic .NET) The generic .NET host is a console application that runs Godot on Windows, Linux, or macOS. A Godot window will open (unless headless). Edit `MyGame.2dog/Program.cs` to add your own host code. It's a simple way to just add C# code around or on top of your Godot game and comfortably run, test, and debug it inside your IDE or using the `dotnet` CLI. ## Use It From your Godot project directory: ```bash dnx 2dog add --generic dotnet run --project MyGame.2dog ``` Pass Godot arguments after `--`. For a headless run: ```bash dotnet run --project MyGame.2dog -- --headless --quit-after 300 ``` ## Publishing ```bash dotnet publish MyGame.2dog -c Release ``` The publish folder includes the executable, .NET runtime, engine, and game pack. Copy the whole folder to the target machine. Add `-r win-x64`, `-r linux-x64`, or `-r osx-arm64` to select a platform. For a build that requires an installed .NET runtime, pass `-p:PublishSelfContained=false`. Single-file publishing is unsupported; Native AOT produces one executable instead. ### Native AOT Native AOT compiles the host, your game, and GodotSharp into one native executable. It needs no .NET runtime and starts faster: ```bash dotnet publish MyGame.2dog -c Release -r win-x64 -p:PublishAot=true ``` The publish folder holds the executable, the engine, and the game pack. To make it the default, set `true` in the host's `.csproj`. See [Native AOT](/configuration#native-aot) for requirements. See [Engine](/api/engine) to customize the frame loop and [Build Variants](/build-configurations) to select an engine build. See [Generic .NET Configuration](/configuration/generic) for build and publish settings. ## No Console End users of generic 2dog apps usually don't need or expect a console window. The generated host already builds Release as `WinExe`, which hides it on Windows: ```xml WinExe ``` Linux and macOS ignore `WinExe`. On Windows, console output then goes nowhere unless it is redirected. --- page: Android section: Hosts source: https://2dog.dev/hosts/android.md description: Package your Godot C# game as an Android APK with the experimental Android host. --- # Android The Android host is a .NET for Android app that packages your game as an APK. Godot's own Activity owns the screen, render loop, input, and lifecycle; the host registers the .NET runtime before Godot starts. ::: warning Experimental Only `android-arm64` and `android-x64` are supported. ::: ## Set It Up ```bash dnx 2dog add --android ``` This creates `MyGame.android/` next to `project.godot`, with the project file, an `Application` that calls `AndroidHost.Register`, and a launcher activity. It also adds an `Android` export preset. Set `ApplicationId` in the project file to your own package name. You also need the .NET `android` workload (the tool offers to install it), an Android SDK, and JDK 17. `2dog new`, `add`, `update`, and `doctor` check `AndroidSdkDirectory`, [`ANDROID_HOME` and `ANDROID_SDK_ROOT`](https://developer.android.com/tools/variables) and common SDK install locations. They warn politely about missing SDKs, incomplete SDK roots, or conflicting environment variables. These checks look for platform tools, platforms, and build tools; they do not verify every project-specific API or build-tools version. The tool leaves your SDK paths and environment variables untouched. See [Android SDK setup](https://learn.microsoft.com/en-us/dotnet/android/getting-started/installation/dependencies) to install the required components. ## Build and Install ```bash dotnet publish MyGame.android -r android-arm64 adb install MyGame.android/bin/Release/net10.0-android/android-arm64/publish/*-Signed.apk ``` Publishing builds the game, exports its pack with the Android preset, and packages a signed APK named after the `ApplicationId`. Use `-r android-x64` for an x64 emulator, and start it with `-gpu swangle` or `-gpu host`. Publishing signs with the first keystore it finds: 1. The .NET `AndroidKeyStore` and `AndroidSigning*` properties 2. Godot's `GODOT_ANDROID_KEYSTORE_RELEASE_PATH`, `_USER`, and `_PASSWORD` environment variables (`DEBUG` variables for Debug builds) 3. Android's debug keystore, `~/.android/debug.keystore` 4. The .NET debug keystore See [Android Configuration](/configuration/android) for build settings. ## Host Code Do not call `Engine.Start()`: Godot runs the loop. To reach the scene from host code, register a listener with `TwoDogActivity.addMainLoopStartedListener`. It runs on Godot's thread once the main scene is ready. ## GDExtensions Extensions need an Android library for each ABI, listed under `android.arm64` and `android.x86_64` keys in their `.gdextension` file. Publishing packages them into the APK. Every listed library must exist, because the pack export copies them. ## Trimming and AOT Release builds are trimmed and AOT-compiled, like any .NET for Android app. 2dog keeps `GodotSharp`, `GodotPlugins`, `twodog`, and your game assembly whole. To keep other assemblies or compile every method ahead of time, see [Android Configuration](/configuration/android). This is Mono's AOT compiler (`RunAOTCompilation`). Experimental [Native AOT](/configuration/android#native-aot) builds compile the app into one native library instead. ## Limitations - Input, pause/resume, and process restarts are not validated yet. --- page: Avalonia section: Hosts source: https://2dog.dev/hosts/avalonia.md description: Embed Godot in a cross-platform Avalonia window with controls over the game. --- # Avalonia The Avalonia host displays Godot inside a cross-platform desktop window using `2dog.avalonia`. Avalonia controls can overlap the game, including translucent controls. ## Use It From your Godot project directory: ```bash dnx 2dog add --avalonia dotnet run --project MyGame.avalonia ``` ## Customize It A `GodotSession` owns the engine and runs frames on the UI thread. `GodotControl` displays the viewport and forwards input. UI event handlers can access game state through `session.Engine.Tree`. See the [showcase host](https://github.com/outfox/2dog/tree/main/demos/showcase/showcase.avalonia) for a pause button, time-scale slider, and FPS display. ## Publish It Publish like the [generic host](./generic#publishing), with or without [Native AOT](./generic#native-aot). See [Avalonia Configuration](/configuration/avalonia) for build and publish settings. ## Requirements - Avalonia 12.1 or later on Windows, Linux, or macOS. - Keep the generated Windows app manifest for correct DPI scaling. - IME composition and captured mouse input are not supported yet. --- page: Blazor section: Hosts source: https://2dog.dev/hosts/blazor.md description: Embed Godot in a Blazor WebAssembly page with GodotView. --- # Blazor The Blazor host embeds Godot in a Blazor WebAssembly page through `GodotView`. An ASP.NET Core server serves the client; Razor components can access Godot objects directly. ## Use It From your Godot project directory, run these commands. Accept `add`'s installation offer if `wasm-tools` is missing: ```bash dnx 2dog add --blazor dotnet run --project MyGame.blazor ``` Open the URL printed by the server. To publish the server and client: ```bash dotnet publish MyGame.blazor -c Release ``` ## Customize the Page Edit `MyGame.blazor/Client/Pages/Home.razor`. Keep WebAssembly rendering and disable prerendering: ```razor @page "/" @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false)) ``` Use `Started` to access the engine, `OnFrame` for per-frame work, and child content to overlay HTML on the game. One engine can run at a time. Godot and Blazor share one thread. The [Web host's limitations](./web#limitations) apply to the client project. See [Blazor Configuration](/configuration/blazor) for client build and pack settings. --- page: Web section: Hosts source: https://2dog.dev/hosts/web.md description: Publish your Godot C# game as a static browser site with the browser-wasm host. --- # Web The Web host is a `browser-wasm` host that runs Godot and your C# game in a browser. It publishes a static site that needs no server-side code. ## Build and Serve Locally From your Godot project directory, run these commands. Accept `add`'s installation offer if `wasm-tools` is missing: ```bash dnx 2dog add --web dotnet publish MyGame.web dnx dotnet-serve -d MyGame.web/AppBundle ``` Open the URL printed by the server. Game output appears in the browser console. Edit `MyGame.web/wwwroot/` to customize the page, then publish again. ## Publishing Upload the contents of `MyGame.web/AppBundle/` to a static web host. Enable gzip or Brotli compression for faster downloads. Cross-origin isolation headers are not required. See [Web Configuration](/configuration/web) for build settings. ## GDExtensions Extensions need a single-threaded WebAssembly side module listed under a `web` key in their `.gdextension` file. With the default `TwoDogExportPack=true`, publishing includes it in `godot.pck`. If you disable export, include the extension in your supplied pack. Side modules cannot use `EM_ASM` or `EM_JS`. ## Limitations - Godot and .NET run on one thread; `System.Threading` is unsupported. - Rendering uses WebGL 2 and Godot's [Compatibility renderer](https://docs.godotengine.org/en/stable/tutorials/rendering/renderers.html). - Audio, fullscreen, and XR follow browser permission and user-gesture rules. For VR or AR, use the [WebXR host](./webxr). To embed the game in a Razor page, use the [Blazor host](./blazor). --- page: WebXR section: Hosts source: https://2dog.dev/hosts/webxr.md description: Run Godot VR and AR in the browser with WebXR and the WebXR Layers polyfill. --- # WebXR The WebXR host is a `browser-wasm` host with Godot's WebXR capabilities enabled and the WebXR Layers polyfill included. See [WebXR Configuration](/configuration/webxr) for build and publish settings. ## Use It From your Godot project directory, run these commands. Accept `add`'s installation offer if `wasm-tools` is missing: ```bash dnx 2dog add --webxr dotnet publish MyGame.webxr ``` Serve the site: ```bash dnx dotnet-serve -d MyGame.webxr/AppBundle ``` ## Set Up Your Scene Enable web XR shaders in `project.godot`: ```ini [xr] shaders/enabled.web=true ``` Add an `XROrigin3D` with an `XRCamera3D`. Start Godot's `WebXRInterface` from a button press and enable `Viewport.UseXR` when the session starts. See the [showcase scene](https://github.com/outfox/2dog/tree/main/demos/showcase) for an example. Use an XR-capable browser and device, or the [Immersive Web Emulator](https://chromewebstore.google.com/detail/immersive-web-emulator/cgffilbpcibhmcfbgggfhfolhkfbhmik). Serve over HTTPS outside `localhost`. The [Web host's limitations](./web#limitations) also apply. --- page: WinForms section: Hosts source: https://2dog.dev/hosts/winforms.md description: Embed Godot in a Windows Forms window. --- # WinForms The WinForms host embeds Godot in a Windows Forms window with a Pause/Resume button. It runs on Windows. Godot reports `Engine.is_embedded_in_editor()` as `true` for this host; account for this in scripts that check editor embedding. ## Use It From your Godot project directory: ```bash dnx 2dog add --winforms dotnet run --project MyGame.winforms ``` ## Customize It Edit the generated form to add controls. Its UI thread runs Godot frames, so event handlers can access the scene tree through `_engine.Tree`. See the [showcase host](https://github.com/outfox/2dog/tree/main/demos/showcase/showcase.winforms). Controls cannot overlap the game window. Use [Avalonia](./avalonia) for overlays or a cross-platform UI. See [WinForms Configuration](/configuration/winforms) for build and publish settings. --- page: WinUI 3 section: Hosts source: https://2dog.dev/hosts/winui.md description: Embed Godot in a Windows App SDK window. --- # WinUI 3 The WinUI 3 host embeds Godot in a Windows App SDK window with a Pause/Resume button. It builds and runs on Windows. Godot reports `Engine.is_embedded_in_editor()` as `true` for this host; account for this in scripts that check editor embedding. ## Use It From your Godot project directory on Windows: ```bash dnx 2dog add --winui dotnet run --project MyGame.winui ``` ## Customize It Edit the generated host to add controls. Its UI thread runs Godot frames, so event handlers can access the scene tree through `_engine.Tree`. See the [showcase host](https://github.com/outfox/2dog/tree/main/demos/showcase/showcase.winui). The app runs unpackaged. WinUI controls cannot overlap the game window; use [Avalonia](./avalonia) for overlays or a cross-platform UI. ## Publish It Publish like the [generic host](./generic#publishing), with or without [Native AOT](./generic#native-aot). See [WinUI 3 Configuration](/configuration/winui) for build and publish settings. --- page: xUnit section: Hosts source: https://2dog.dev/hosts/xunit.md description: Run tests against your Godot project with the 2dog.xunit host. --- # xUnit The xUnit host runs tests against your Godot project using a real engine. Tests run headless by default; an xUnit fixture owns the engine's lifetime. ## Use It From your Godot project directory: ```bash dnx 2dog add --tests dotnet test MyGame.tests ``` Edit `MyGame.tests/BasicTests.cs` to add tests. Use `HeadlessFixture` for headless tests or `Fixture` for rendered tests. See [Testing with xUnit](/testing) for examples, collections, and CI setup. See [xUnit Configuration](/configuration/xunit) for native variants and Editor tests. ## Shared State Tests in a collection share one engine. Keep collection parallelism disabled and free nodes you create. See [Single Godot Instance](/known-issues/single-instance) for engine lifetime rules. --- page: API Reference section: API Reference source: https://2dog.dev/api-reference.md description: Overview of the deliberately small 2dog API: twodog.Engine, Godot.GodotInstance, the twodog.Testing fixtures, xUnit collections, and object lifetimes at a glance. --- # API Reference The 2dog API is deliberately small. Use `twodog.Engine` to own one Godot instance, or use the fixture classes when xUnit owns the process. ## `twodog` | Type | Use it to | | --- | --- | | [`Engine`](./api/engine) | Configure, start, run, and stop embedded Godot | `Engine` is the normal entry point from the `2dog.engine` package. ## `Godot` | Type | Use it to | | --- | --- | | [`GodotInstance`](./api/godot-instance) | Access the borrowed compatibility handle returned by `Engine.Start()` | 2dog adds `GodotInstance` to the GodotSharp API. Use the [Godot class reference](https://docs.godotengine.org/en/latest/classes/) for nodes, resources, scenes, and other engine types. ## `twodog.Testing` | Type | Use it to | | --- | --- | | [`FixtureBase`](./api/fixture-base) | Build a fixture with custom Godot arguments | | [`Fixture`](./api/fixture) | Run tests with rendering enabled | | [`HeadlessFixture`](./api/headless-fixture) | Run headless tests, including CI | These fixtures ship in `2dog.engine` and do not depend on a specific test framework. ## `twodog.Testing.Xunit` | Type | Use it to | | --- | --- | | [`RenderingCollection`](./api/rendering-collection) | Share a rendered fixture across an xUnit collection | | [`HeadlessCollection`](./api/headless-collection) | Share a headless fixture across an xUnit collection | The collection definitions ship in `2dog.xunit` and compile into the consuming test assembly so xUnit can discover them. ## Lifetime at a Glance ```csharp using var engine = new twodog.Engine("MyGame", args: args); engine.Start(); while (!engine.Iteration()) { // One frame has completed. } ``` `Engine` owns the instance. Use `RequestQuit()` for graceful shutdown; `Completion` completes and `Exited` fires after teardown on every platform. Desktop `Dispose()` is synchronous; browser callers await `DisposeAsync()` or `Completion`. Start a new `Engine` only after completion. See [Single Godot Instance](./known-issues/single-instance). --- page: twodog.Engine section: API Reference source: https://2dog.dev/api/engine.md description: API reference for twodog.Engine, which configures, starts, and owns one embedded Godot instance: constructor, properties, Start, Run, Dispose, and common Godot arguments. --- # `twodog.Engine` Configures, starts, and owns one embedded Godot instance. ```csharp public class Engine : IDisposable, IAsyncDisposable ``` **Package:** `2dog.engine` **Namespace:** `twodog` ## Constructor ```csharp public Engine(string project, string? path = null, params string[] args) ``` | Parameter | Description | | --- | --- | | `project` | Label passed as Godot's first argument; this is not a content path | | `path` | Directory containing `project.godot`; omit it for automatic content resolution | | `args` | Additional Godot command-line arguments, passed unchanged | When `path` is omitted or `null`, desktop hosts call [`ResolveContent()`](#resolvecontent). Browser hosts leave the path unset so Godot loads the web pack. Pass a path only for a nonstandard content location. Relative paths are resolved from the process working directory. ## Properties ### `Tree` ```csharp public SceneTree Tree { get; } ``` Returns the active scene tree. Access it only after `Start()` succeeds. ### `NativePath` ```csharp public string? NativePath { get; init; } ``` Loads an exact desktop `libgodot` file instead of selecting a packaged variant. Leave this unset in normal hosts. It is not supported in the browser. ### `ProjectAssemblyDir` ```csharp public string? ProjectAssemblyDir { get; init; } ``` Sets the preferred directory for the game's C# assembly. The default is `AppContext.BaseDirectory`; custom isolated hosts may need a different path. ### `LoadedNativePath` ```csharp public static string? LoadedNativePath { get; } ``` Returns the full path of the loaded desktop `libgodot`, when known. ### `Completion` ```csharp public Task Completion { get; } ``` Completes when this `Engine` reaches a terminal state. If `Start()` succeeded, that means the owned native instance has been destroyed and synchronous `Exited` handlers have returned. Asynchronous work started by those handlers is not awaited. Shutdown restores the host synchronization context before notifying `Exited`, so subsequent host awaits do not depend on Godot's stopped frame loop. ### `Exited` ```csharp public event Action? Exited; ``` Fires once after a successfully started instance is destroyed. `Completion` also completes for an `Engine` disposed before it starts, but `Exited` does not fire. ## Methods ### `Start` ```csharp public GodotInstance Start() ``` Starts Godot and the project's `run/main_scene`, then returns a borrowed `GodotInstance` compatibility handle. `Engine` owns the instance; do not dispose the handle. Starting another instance before completion throws `InvalidOperationException`. ### `Iteration` ```csharp public bool Iteration() ``` Processes one main-loop frame. Returns `true` when Godot requests exit. Dispose the engine after the pump stops. Calling `Iteration()` recursively, or while `Run()` owns the pump, throws `InvalidOperationException`. ### `RequestQuit` ```csharp public void RequestQuit() ``` Requests a graceful quit. Repeated requests are harmless. ### `Run` ```csharp public void Run(Action? perFrame = null) ``` Runs the main loop after `Start()`. - On desktop, it blocks until Godot requests exit. `perFrame` runs after each completed frame that did not request exit. - In the browser, it registers the Emscripten main loop and returns immediately. ### `Dispose` ```csharp public void Dispose() ``` Stops and destroys the owned instance synchronously on desktop. In a browser, `Dispose()` requests shutdown and returns before teardown; use `DisposeAsync()` or await `Completion`. ### `DisposeAsync` ```csharp public ValueTask DisposeAsync() ``` Requests shutdown and waits for teardown. Prefer this in browser hosts. Lifecycle calls and Godot object access belong on the thread that called `Start()`. Calls from another thread are rejected; dispatch them to the host's engine thread. Disposal from a game callback is deferred until the native frame returns. Do not synchronously wait for `Completion` inside that callback. ### `WebExitRuntimeOnQuit` ```csharp public static bool WebExitRuntimeOnQuit { get; set; } ``` Defaults to `false` in a browser: quitting destroys Godot and keeps .NET alive so any host can start another engine. Standalone pages may set it to `true` to terminate the entire WebAssembly runtime on quit. Blazor keeps it `false`. Browser restarts require a fresh canvas and host configuration before `Start()`; `GodotView` handles those browser resources automatically. ### `WebLogFirstChanceExceptions` ```csharp public static bool WebLogFirstChanceExceptions { get; set; } ``` Defaults to `false`. Set to `true` before browser startup to print every thrown managed exception to the browser console as an error, including caught exceptions and exceptions stored in unawaited tasks. Set back to `false` after investigating; this diagnostic mode adds logging overhead. Setting it on desktop throws `PlatformNotSupportedException`. Unhandled managed exceptions and unobserved task exceptions are logged automatically on browser from the first use of `Engine`, across restarts. Unobserved task reporting waits for garbage collection. ### `ResolveContent` ```csharp public static string? ResolveContent() ``` Returns `null` when a `.pck` named after the executable sits beside it, letting Godot load that pack. Otherwise, returns [`ResolveProjectDir()`](#resolveprojectdir). The desktop constructor calls this automatically when `path` is omitted. ### `ResolveProjectDir` ```csharp public static string ResolveProjectDir() ``` Returns the absolute `GodotProjectDir` embedded in loaded assembly metadata. It throws `InvalidOperationException` when no loaded assembly provides that metadata. ### `RegisterWebPluginsInitializer` ```csharp public static void RegisterWebPluginsInitializer(IntPtr initializer) ``` Registers the game assembly's generated plugin initializer in a browser host. The web host's `Program.cs` calls this before `Start()` with the pointer from `TwoDogWebBoot.PluginsInitializer()` (the bootstrap file in the web host folder, compiled into the game assembly); normal application code does not need anything beyond that template line. Desktop calls throw `PlatformNotSupportedException`. ## Example ```csharp using Godot; using Engine = twodog.Engine; internal static class Program { [STAThread] private static void Main(string[] args) { using var engine = new Engine("MyGame", args: args); engine.Start(); while (!engine.Iteration()) { // One frame has completed. } } } ``` `args` accepts ordinary [Godot command-line arguments](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html) – `--headless`, `--verbose`, `--rendering-driver opengl3`, `--audio-driver Dummy`, and the rest – and passes them to the engine unchanged. See [2dog (generic .NET)](../hosts/generic) to run and publish a desktop host. --- page: Godot.GodotInstance section: API Reference source: https://2dog.dev/api/godot-instance.md description: API reference for Godot.GodotInstance, the borrowed compatibility handle returned by Engine.Start(). --- # `Godot.GodotInstance` Borrowed compatibility handle returned by [`twodog.Engine.Start()`](./engine#start). ```csharp public class GodotInstance : IDisposable ``` **Package:** `2dog.engine` **Namespace:** `Godot` This class is one of the few differences from stock Godot, where it's not exposed. Its `Engine` owns the native instance and its lifecycle. Create this object through `twodog.Engine.Start()` rather than its low-level static factory methods. ## Compatibility Methods ### `Iteration` ```csharp public bool Iteration() ``` Processes one main-loop frame. Returns `true` when Godot wants to quit. New host code should call `Engine.Iteration()`. ```csharp while (!engine.Iteration()) { // One frame has completed. } ``` ### `IsStarted` ```csharp public bool IsStarted() ``` Returns whether this instance has started. ### `FocusIn` and `FocusOut` ```csharp public void FocusIn() public void FocusOut() ``` Notify Godot that the host has gained or lost focus. Ordinary generic hosts let Godot's display server handle focus and do not call these methods directly. ### `Pause` and `Resume` ```csharp public void Pause() public void Resume() ``` Notify Godot that the host application has paused or resumed. These lifecycle hooks are useful when another application framework owns the outer window. ::: warning Choose one loop owner Call `Engine.Iteration()` yourself or call `Engine.Run()`. Do not use both for the same instance. Use `Engine.RequestQuit()`, `Completion`, `Exited`, and engine disposal for lifecycle control; do not dispose this borrowed handle. ::: --- page: Godot.GodotSharp section: API Reference source: https://2dog.dev/api/godotsharp.md description: API stub for GodotSharp, which 2dog exposes. --- # `using Godot;` 2dog exposes the full GodotSharp API that we've grown to love and expect. You can just code like you're used to, both in your game-side code, and the host-side code. Of course you can also work with GDscript (game-side only). :::info Official Docs I'm happy to wag my tail send you on an adventure to godotengine.org! https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp ::: --- page: twodog.Testing.FixtureBase section: API Reference source: https://2dog.dev/api/fixture-base.md description: API reference for FixtureBase, the abstract base class for test fixtures that own a Godot instance - constructor, properties, disposal, and custom xUnit collections. --- # `twodog.Testing.FixtureBase` Base class for test fixtures that own a Godot instance. ```csharp public abstract class FixtureBase : IDisposable ``` **Package:** `2dog.engine` **Namespace:** `twodog.Testing` The constructor resolves the Godot project directory, preloads game assemblies, and starts the engine. Derive from this class when the ready-made fixtures do not pass the arguments your tests need. ## Constructor ```csharp protected FixtureBase(params string[] cmdLineArgs) ``` Passes each argument to Godot unchanged. ```csharp public class OpenGl3Fixture() : FixtureBase("--rendering-driver", "opengl3"); ``` ## Properties | Property | Type | Description | | --- | --- | --- | | `Engine` | `twodog.Engine` | Engine owned by the fixture | | `GodotInstance` | `Godot.GodotInstance` | Borrowed compatibility handle | | `Tree` | `Godot.SceneTree` | Active scene tree | | `Errors` | `twodog.GodotErrorLog` | Errors and warnings Godot reported and no test consumed yet | ## `Errors` The fixture starts its engine with `CaptureErrors` enabled, so every error and warning Godot reports lands in `Errors`: `push_error`, failed engine checks, and exceptions Godot catches in C# callbacks. With `2dog.xunit`, a test fails when it leaves any behind. A test that expects one consumes it, asserting its text: ```csharp godot.Errors.Expect("Health must not be negative"); ``` `Drain()` returns and clears the log without asserting anything. ## `Dispose` ```csharp public void Dispose() ``` Disposes the owning `Engine`. Let the test framework call this through its fixture lifetime. ## Custom xUnit Collection ```csharp using twodog.Testing; using Xunit; public class OpenGl3Fixture() : FixtureBase("--rendering-driver", "opengl3"); [CollectionDefinition(nameof(OpenGl3Collection), DisableParallelization = true)] public class OpenGl3Collection : ICollectionFixture; ``` Godot is not thread-safe. Keep custom engine collections non-parallel. --- page: twodog.Testing.Fixture section: API Reference source: https://2dog.dev/api/fixture.md description: API reference for Fixture, the test fixture that starts Godot with rendering enabled - used through RenderingCollection in xUnit tests. --- # `twodog.Testing.Fixture` Starts a Godot test fixture with rendering enabled. ```csharp public class Fixture : FixtureBase ``` **Package:** `2dog.engine` **Namespace:** `twodog.Testing` Use this fixture for tests that need a display, rendered frames, or window behavior. For most CI tests, prefer [`HeadlessFixture`](./headless-fixture). ## Inherited Properties | Property | Type | Description | | --- | --- | --- | | `Engine` | `twodog.Engine` | Engine owned by the fixture | | `GodotInstance` | `Godot.GodotInstance` | Running native instance | | `Tree` | `Godot.SceneTree` | Active scene tree | ## xUnit Collection [`RenderingCollection`](./rendering-collection) supplies this fixture to a non-parallel xUnit collection. ```csharp using twodog.Testing; using twodog.Testing.Xunit; using Xunit; [Collection] public class RenderingTests(Fixture godot) { [Fact] public void SceneTree_IsRunning() => Assert.NotNull(godot.Tree.Root); } ``` --- page: twodog.Testing.HeadlessFixture section: API Reference source: https://2dog.dev/api/headless-fixture.md description: API reference for HeadlessFixture, the test fixture that starts Godot with --headless - used through HeadlessCollection in xUnit tests. --- # `twodog.Testing.HeadlessFixture` Starts a Godot test fixture with `--headless`. ```csharp public class HeadlessFixture : FixtureBase ``` **Package:** `2dog.engine` **Namespace:** `twodog.Testing` This is the usual fixture for game-logic, scene, resource, and CI tests. Use [`Fixture`](./fixture) when a test needs rendering or a window. ## Inherited Properties | Property | Type | Description | | --- | --- | --- | | `Engine` | `twodog.Engine` | Engine owned by the fixture | | `GodotInstance` | `Godot.GodotInstance` | Running native instance | | `Tree` | `Godot.SceneTree` | Active scene tree | ## xUnit Collection [`HeadlessCollection`](./headless-collection) supplies this fixture to a non-parallel xUnit collection. ```csharp using twodog.Testing; using twodog.Testing.Xunit; using Xunit; [Collection] public class SceneTests(HeadlessFixture godot) { [Fact] public void SceneTree_IsRunning() => Assert.NotNull(godot.Tree.Root); } ``` --- page: twodog.Testing.Xunit.RenderingCollection section: API Reference source: https://2dog.dev/api/rendering-collection.md description: API reference for RenderingCollection, the non-parallel xUnit collection that shares one rendering Fixture across test classes. --- # `twodog.Testing.Xunit.RenderingCollection` Defines a non-parallel xUnit collection backed by [`Fixture`](./fixture). ```csharp public class RenderingCollection : ICollectionFixture ``` **Package:** `2dog.xunit` **Namespace:** `twodog.Testing.Xunit` The package compiles this collection definition into your test assembly so xUnit can discover it. ## Usage ```csharp using twodog.Testing; using twodog.Testing.Xunit; using Xunit; [Collection] public class RenderingTests(Fixture godot) { [Fact] public void SceneTree_IsRunning() => Assert.NotNull(godot.Tree.Root); } ``` Tests in the collection share one fixture. Clean up nodes and other state that should not carry into the next test. --- page: twodog.Testing.Xunit.HeadlessCollection section: API Reference source: https://2dog.dev/api/headless-collection.md description: API reference for HeadlessCollection, the non-parallel xUnit collection backed by one shared HeadlessFixture. --- # `twodog.Testing.Xunit.HeadlessCollection` Defines a non-parallel xUnit collection backed by [`HeadlessFixture`](./headless-fixture). ```csharp public class HeadlessCollection : ICollectionFixture ``` **Package:** `2dog.xunit` **Namespace:** `twodog.Testing.Xunit` The package compiles this collection definition into your test assembly so xUnit can discover it. This is the usual collection for CI. ## Usage ```csharp using twodog.Testing; using twodog.Testing.Xunit; using Xunit; [Collection] public class SceneTests(HeadlessFixture godot) { [Fact] public void SceneTree_IsRunning() => Assert.NotNull(godot.Tree.Root); } ``` Tests in the collection share one fixture. Clean up nodes and other state that should not carry into the next test. --- page: twodog.fixture.AssemblyPreloader section: API Reference source: https://2dog.dev/api/assembly-preloader.md description: API reference for AssemblyPreloader, which preloads game assemblies into the default AssemblyLoadContext before Godot starts. --- # `twodog.fixture.AssemblyPreloader` Preloads game assemblies into the default `AssemblyLoadContext` before Godot starts. ```csharp public static class AssemblyPreloader ``` **Package:** `2dog.engine` **Namespace:** `twodog.fixture` ::: info Advanced API The supplied fixtures call this automatically. Use it only in a custom host that references game types directly and does not use `FixtureBase`. ::: ## `PreloadGameAssemblies` ```csharp public static void PreloadGameAssemblies(string projectPath) ``` Finds compiled game assemblies under the project's `.godot/mono/temp/bin/{Configuration}` directory and loads them into the default context. This prevents the same game type from being loaded with a second type identity in Godot's plugin context. When the host references the game project, the host's own copy is loaded, so it always matches the host's build configuration; other configurations' outputs and leftovers of renamed projects are ignored. Otherwise the first of the `Debug`, `Release` and `Editor` folders that contains assemblies is loaded. | Parameter | Description | | --- | --- | | `projectPath` | Directory containing `project.godot` | Call it before `Engine.Start()`. In the browser it returns without doing work. Discovery and load failures are written to the console instead of thrown; one assembly that fails to load does not stop the others. ```csharp var projectDir = twodog.Engine.ResolveProjectDir(); AssemblyPreloader.PreloadGameAssemblies(projectDir); using var engine = new twodog.Engine("MyGame", args: ["--headless"]); engine.Start(); ``` --- page: dnx 2dog section: Develop and Configure source: https://2dog.dev/dnx-2dog.md description: Reference for the 2dog command-line tool: installation, commands, host flags, global and output options, exit codes, version pinning. --- # `dotnet tool execute 2dog` `2dog` scaffolds .NET host projects around a Godot project, keeps them healthy, and updates them. The project directory becomes the solution root; hosts live in nested folders Godot ignores. It creates files and edits `*.csproj`, `project.godot`, the solution and `Directory.Build.props` in place. Nothing is moved, renamed or deleted, except two announced opt-ins: the `.sln` → `.slnx` migration and the [`--rename` fix](/known-issues/spaced-project-names). The tool and the matching `dotnet new` template ship together in the [`2dog` NuGet package](https://www.nuget.org/packages/2dog/). ## Installation ```bash dnx 2dog add # run latest from nuget dotnet tool install -g 2dog # or install it globally ``` ## Commands | Command | Use it to | | --- | --- | | [`2dog new`](/cli/new) | Create a Godot project with 2dog hosts | | [`2dog add`](/cli/add) | Add hosts to an existing Godot project (`2dog convert` is an alias) | | [`2dog doctor`](/cli/doctor) | Check the project and this machine, fix what can be fixed, explain build failures | | [`2dog update`](/cli/update) | Bring the project's 2dog packages to this tool's versions | | [`2dog pack`](/cli/pack) | List a `.pck`'s contents | | [`2dog version`](/cli/version) | Print the tool and package versions | | [`2dog help`](/cli/help) | Show the usage, or the help for one verb | `2dog` alone prints the version info and the usage. `new`, `add` and `doctor` prompt on a terminal when no deciding flag is given. Host flags skip the project wizard; a missing workload (`wasm-tools`, `android`) can still be offered before restore. `--yes`, pipes, and CI skip workload offers. Use `--install-wasm-tools` or `--install-android-workload` to request installation explicitly, or `2dog update --update-workloads` to update workloads. ## Host flags `new` and `add` take one flag per host. The folder is optional; a repeated flag adds a second host of the same kind. | Flag | Host | | --- | --- | | `--generic [folder]` | [2dog (generic .NET)](/hosts/generic) with your own `Main()` | | `--web [folder]` | [Web host](/hosts/web) | | `--webxr [folder]` | [WebXR host](/hosts/webxr); opt-in | | `--tests [folder]` | [xUnit test project](/hosts/xunit) | | `--winforms [folder]` | [WinForms host](/hosts/winforms); Windows-only, opt-in | | `--winui [folder]` | [WinUI 3 host](/hosts/winui); Windows-only, builds only on Windows, opt-in | | `--avalonia [folder]` | [Avalonia host](/hosts/avalonia); opt-in | | `--blazor [folder]` | [Blazor Web App host](/hosts/blazor); opt-in, needs wasm-tools | | `--android [folder]` | [Android host](/hosts/android); experimental, opt-in, needs the android workload | | `--no-generic`, `--no-web`, `--no-tests` | Leave a host out of the default set (every kind has a `--no-` form; opt-in kinds are never in the set) | ## Global options Valid under every verb, also before it (`2dog --json add`). | Option | Effect | | --- | --- | | `-y, --yes` | Do not prompt; take the flags and defaults (also `--non-interactive`, `--no-input`) | | `-h, --help` | Show help; after a verb, the help for that verb | | `--version` | Same as [`2dog version`](/cli/version); see [choosing a tool version](/cli/pinning#run-a-specific-tool-version) | Values attach or follow: `--name Foo`, `--name=Foo`. `--` ends option parsing. Mistyped options and verbs get a suggestion. ## Output | Option | Effect | | --- | --- | | `--json` | One JSON document on stdout, nothing on stderr, also on failure (`"ok": false`); implies `--yes` | | `-q, --quiet` | Results and problems only: no header, plan, progress or next steps | | `--plain` | No colour, no cursor movement, ASCII markers (also `TERM=dumb`) | | `--no-color` | No colour; cursor movement stays (also `NO_COLOR`) | | `--accessible` | Numbered yes/no questions instead of lists, no spinners (also `TWODOG_ACCESSIBLE=1`) | | `-v, --verbose` | Extra detail on stderr: subprocess command lines and output, stack traces | The report goes to stdout, diagnostics (`error:`, `warning:`, `note:`, `hint:`, `verbose:`, spinners) to stderr: `2dog add --json | jq .` and `2dog doctor 2>errors.log` both work. In a pipe or a CI environment (`CI`, `GITHUB_ACTIONS`, `TF_BUILD`, `GITLAB_CI`, `TEAMCITY_VERSION`, `BUILD_NUMBER`) nothing prompts or animates and the markers are ASCII; a wanted prompt falls back to the default with a `note:`. `CLICOLOR_FORCE` or `FORCE_COLOR` keeps colour in a pipe. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Success, including `--dry-run`, "Nothing to do", and a declined plan | | `1` | Usage error: unknown option or verb, missing value, wrong positional count; the error names the verb's `--help` | | `2` | Tool error: project state, I/O, invalid XML, a failed subprocess, a plan that stopped half-way (the report says which steps stand), an unexpected exception (`--verbose` adds the stack trace) | | `3` | `doctor`: findings remain after the fixes (errors; warnings too under `--strict`), or the `--build` failed | | `130` | Cancelled with Ctrl+C | ## Versions The [Version guide](/cli/pinning) explains how to run a specific tool release, print its version, and keep your project's package versions fixed. --- page: 2dog add section: Develop and Configure source: https://2dog.dev/cli/add.md description: Reference for 2dog add: add .NET hosts to an existing Godot project in place - arguments, host flags, options, examples. --- # `2dog add` Adds hosts to an existing Godot project, in place. Run it again to add more hosts, including a second host of the same kind. Without host flags it asks which hosts to add. Host flags skip that wizard; on terminals, a missing workload (`wasm-tools`, `android`) can still be offered. `-y` runs unattended. ```bash 2dog add [path] [hosts] [options] ``` | Argument | Meaning | | --- | --- | | `path` | Directory containing `project.godot`; defaults to the current directory | ## Hosts Any [host flag](/dnx-2dog#host-flags), repeatable. Unattended without one: generic, browser and tests, minus the `--no-` ones. Existing hosts are recognized and skipped. The new hosts are built for the running tool's versions, so the game project's `Godot.NET.Sdk` and an existing 2dog version block in `Directory.Build.props` are raised to them in the same run (never lowered), as `2dog update` would. A move across Godot lines is called out: install the matching editor and open the project once. Migrating an existing `.sln` to `.slnx` first saves a `.sln.old` backup beside it, using `.sln.old.1`, `.sln.old.2`, and so on if needed. Existing backups are kept; the `.sln` is removed only after conversion succeeds. ## Options | Option | Effect | | --- | --- | | `-n, --name ` | Base name override for the scaffolded files; letters, digits, `.` and `_` survive, `-` becomes `_`, C# keywords get a `_` prefix | | `--rename ` | Rename a .NET project name that [contains spaces](/known-issues/spaced-project-names), then scaffold; only before any hosts exist | | `--dry-run` | Print the plan; change nothing | | `--force` | Overwrite scaffolded files that exist; never deletes | | `--no-restore` | Skip the final `dotnet restore` | | `--install-wasm-tools` | Install missing `wasm-tools` for browser hosts without an installation prompt | | `--install-android-workload` | Install the missing `android` workload for Android hosts without an installation prompt | Plus the [global and output options](/dnx-2dog#global-options). Before restore, browser hosts get a `wasm-tools` installation offer on terminals, and Android hosts an `android` one. `--no-restore` skips the offers; the install flags request installation explicitly, including in CI. `--dry-run` never checks or installs workloads. ## Examples Install the [.NET 10 SDK](https://dotnet.microsoft.com/download), then add and run a generic .NET host in your existing Godot project: ```bash cd path/to/MyGame dnx 2dog add --generic dotnet run --project MyGame.2dog ``` 2dog creates a nested host project that runs your game. Keep editing scenes and scripts in Godot as usual. GDScript projects work too. Choose hosts interactively, or supply host flags: ```bash 2dog add # interactive, here 2dog add --generic MyGame.editor # a second generic host, named 2dog add --android # an Android host 2dog add path/to/project --no-web 2dog add --rename MyGame # fix a spaced .NET name first ``` See [Hosts](/hosts/) for host types and [Project Layout](/project-layout) for generated files. For a new project, use [`2dog new`](/cli/new). --- page: 2dog new section: Develop and Configure source: https://2dog.dev/cli/new.md description: Reference for 2dog new: create a Godot project with 2dog hosts - arguments, host flags, options, examples. --- # `2dog new` Creates a new Godot project with 2dog hosts. Without host flags it asks which hosts to create. Host flags skip that wizard; a missing workload (`wasm-tools`, `android`) can still be offered on terminals. `-y` runs unattended. ```bash 2dog new [Name] [dir] [hosts] [options] ``` | Argument | Meaning | | --- | --- | | `Name` | Project name, used for folders, assemblies and namespaces; letters, digits, `.` and `_` survive, `-` becomes `_`, C# keywords get a `_` prefix, an adjustment is announced | | `dir` | Directory to create; defaults to the sanitized name | ## Hosts Any [host flag](/dnx-2dog#host-flags), repeatable. Unattended without one: generic, browser and tests, minus the `--no-` ones. ## Options | Option | Effect | | --- | --- | | `-n, --name ` | Project name; same as `Name` | | `-o, --output ` | Directory; same as `dir` | | `--dry-run` | Print the plan; change nothing | | `--force` | Overwrite scaffolded files that exist; never deletes | | `--no-restore` | Skip the final `dotnet restore` | | `--install-wasm-tools` | Install missing `wasm-tools` for browser hosts without an installation prompt | | `--install-android-workload` | Install the missing `android` workload for Android hosts without an installation prompt | Plus the [global and output options](/dnx-2dog#global-options). `--no-restore` skips the workload offers; the install flags request installation explicitly. `--dry-run` never checks or installs workloads. ## Examples Install the [.NET 10 SDK](https://dotnet.microsoft.com/download), then create and run a project with a generic .NET host: ```bash dnx 2dog new MyGame --generic cd MyGame dotnet run --project MyGame.2dog ``` Open `project.godot` in the Godot .NET editor to edit scenes and scripts. See [Project Layout](/project-layout) for the generated files and [Hosts](/hosts/) for host types. Choose hosts interactively, or supply host flags: ```bash 2dog new MyGame # interactive host choice 2dog new MyGame --generic --tests # unattended 2dog new "My Game" -o games/mine --no-web # name adjusted to MyGame ``` For an existing project, use [`2dog add`](/cli/add). ## Use the .NET Template The same template is available through `dotnet new`: ```bash dotnet new install 2dog dotnet new 2dog -n MyGame --web false ``` This creates generic .NET and xUnit hosts. Omit `--web false` to include Web; install `wasm-tools` first. ## Debugging the Godot project in VS Code New projects include `.vscode/launch.json`, `tasks.json`, and a recommendation for Microsoft's C# extension. Open the project root folder, install that extension, select **Godot: Play (C#)** in Run and Debug, and press F5. The build task builds only the root Godot project in Debug configuration. Set the `GODOT4` environment variable to the main **.NET-enabled** Godot executable and restart VS Code, or replace `program` in `launch.json` with its absolute path. On Windows, use the `mono` executable, not the `_console.exe` wrapper. Godot Tools' editor path setting alone does not configure the C# debugger. The Godot SDK root is a library loaded by Godot, so it needs this external executable launch. The `.2dog` host is a regular .NET executable and supports the usual .NET launch workflow. **Godot: Attach (C#)** can attach to an already running .NET Godot game or editor; select the process that loads your scripts. --- page: 2dog doctor section: Develop and Configure source: https://2dog.dev/cli/doctor.md description: Reference for 2dog doctor: check a 2dog project and the machine, apply fixes, explain build failures - options, exit codes, every check id. --- # `2dog doctor` Checks the project and this machine without building, and applies the fixes it can. Safe repairs use `--fix`; announced changes use `--fix-all` or the interactive checklist. ```bash 2dog doctor [path] [options] ``` | Argument | Meaning | | --- | --- | | `path` | Directory containing `project.godot`; defaults to the current directory | ## Options | Option | Effect | | --- | --- | | `--fix` | Apply the safe fixes, then check again | | `--fix-all` | Also apply announced fixes, including missing workload installation | | `--install-wasm-tools` | Install missing `wasm-tools` for browser hosts, then re-check | | `--install-android-workload` | Install the missing `android` workload for Android hosts, then re-check | | `--build [target]` | Run `dotnet build` of the solution, or a host folder or project, and explain known failures | | `-c, --configuration ` | Configuration for `--build` (default `Debug`) | | `--log ` | Only explain an existing build, restore or runtime log; `-` reads stdin | | `--ignore ` | Drop a finding by [check id](#checks); repeatable | | `--strict` | Warnings count as findings for the exit code | | `--offline` | Skip the nuget.org check for a newer tool | | `--list-checks` | Print every check and build-log signature | Plus the [global and output options](/dnx-2dog#global-options); `-v` lists the passed checks too. Installing a workload is an announced fix: select it in the checklist, or use its install flag or `--fix-all`. `--fix` alone does not install workloads. Solution migration first saves a `.sln.old` backup beside the original, numbered `.sln.old.1`, `.sln.old.2`, and so on if needed. Existing backups are kept; the `.sln` is removed only after conversion to `.slnx` succeeds. ## Exit codes | Code | Meaning | | --- | --- | | `0` | Only informational items remain | | `2` | Doctor could not run: no `project.godot`, unreadable files | | `3` | Errors remain (warnings too under `--strict`), or the `--build` failed | ## Examples ```bash 2dog doctor # check, then ask which fixes to apply 2dog doctor --fix # apply the safe fixes unattended 2dog doctor --build MyGame.web -c Release # build one host and explain failures 2dog doctor --log build.log # explain an existing log dotnet build 2>&1 | 2dog doctor --log - # or a piped one 2dog doctor --json --strict | jq '.doctor.summary' # CI: exit 3 on any warning ``` ## Checks Ids are stable; `--ignore ` drops one. ### Environment | Id | Passes when | | --- | --- | | `env.dotnet-sdk` | a .NET 10 SDK is installed | | `env.global-json` | the root global.json pin is satisfied by an installed SDK | | `env.wasm-tools` | the wasm-tools workload is installed when a browser host exists | | `env.android-workload` | the android workload is installed when an Android host exists | | `env.android-sdk` | Android SDK paths and components are available when an Android host exists | | `env.host-platform` | this OS and architecture have 2dog native packages | | `env.godot-editor` | GODOT_EDITOR, when set, points at an existing file | | `env.godot4` | GODOT4 points at an existing Godot executable for IDE debugging | | `env.overrides` | GODOTSHARP_DIR and the other layout overrides point at what they claim | | `env.packages-restored` | the engine, tools and native packages are in the NuGet cache | ### Layout | Id | Passes when | | --- | --- | | `layout.load-problems` | every project file parses | | `layout.game-csproj` | the game csproj exists at the project root | | `layout.assembly-name` | project.godot names the assembly and the csproj matches it | | `layout.multiple-root-csproj` | a single csproj at the root, or assembly_name picks one | | `layout.spaced-name` | the .NET project name contains no whitespace | | `layout.legacy-root-webboot` | TwoDogWebBoot.cs lives in a web host folder, not at the root | | `layout.root-build-targets` | Directory.Build.targets provides the deep-clean target | | `layout.root-build-props` | Directory.Build.props holds the shared package versions | | `layout.root-global-json` | global.json pins a wasm-capable SDK when a browser host exists | ### Game csproj | Id | Passes when | | --- | --- | | `game.sdk` | the game csproj uses Godot.NET.Sdk | | `game.sdk-mismatch` | Godot.NET.Sdk matches the Godot line 2dog.engine was built for | | `game.target-framework` | the game csproj targets net10.0 | | `game.properties` | EnableDynamicLoading, AllowUnsafeBlocks and TWODOG_WEB_BOOT are set | | `game.default-item-excludes` | every host folder is excluded from the game's default globs | | `game.webboot-include` | the web bootstrap is compiled into the game assembly | | `game.webboot-duplicate` | exactly one TwoDogWebBoot.cs compiles into the game assembly | ### Hosts | Id | Passes when | | --- | --- | | `host.gdignore` | every host folder carries a .gdignore | | `host.project-reference` | every ProjectReference points at an existing project | | `host.godot-project-dir` | GodotProjectDir points at the Godot project | | `host.variant` | TwoDogVariant is release, debug or editor | | `host.buildtype-deprecated` | the deprecated TwoDogBuildType property is gone | | `host.publish-aot` | no WinForms, browser or Blazor host enables PublishAot | | `host.publish-singlefile` | PublishSingleFile is only enabled by browser hosts or alongside PublishAot | | `host.duplicate-analyzers` | hosts referencing the game strip the duplicate Godot analyzers | | `host.app-manifest` | the app.manifest a host declares exists | | `host.web-props-shim` | browser hosts chain to the root Directory.Build.props | | `host.web-global-json` | browser hosts carry their own global.json | | `host.webboot-drift` | the tool-owned TwoDogWebBoot.cs matches this tool's copy | | `host.trimmer-root` | browser hosts root the game assembly for the trimmer | | `host.blazor-client` | the Blazor client project exists and publishes 2dog.engine | | `host.blazor-launch-settings` | Blazor hosts carry a Development launchSettings.json | | `host.windows-only` | Windows-only hosts are noted on other platforms | ### Solution | Id | Passes when | | --- | --- | | `sln.exists` | a solution exists at the project root | | `sln.multiple` | exactly one solution contains the game project | | `sln.legacy-format` | the solution uses the .slnx format | | `sln.contains-game` | the solution lists the game project | | `sln.contains-hosts` | the solution lists every host project | | `sln.build-exclusions` | browser and WinUI hosts are excluded from plain solution builds | ### Versions | Id | Passes when | | --- | --- | | `ver.managed-elsewhere` | versions expressed through your own properties are noted, not judged | | `ver.props-invalid` | the TwoDog* versions in Directory.Build.props are readable versions | | `ver.literal-versions` | host csprojs use the shared version properties, not literals | | `ver.twodog-consistent` | every 2dog package uses the same version | | `ver.twodog-outdated` | the 2dog packages are at this tool's version | | `ver.twodog-newer` | the project is not ahead of this tool | | `ver.natives` | the native packages are exact-pinned on the engine's Godot line | | `ver.godot-line-consistent` | TwoDogGodotVersion matches the game project's Godot.NET.Sdk | | `ver.godotsharp-editor` | GodotSharpEditor in the test host matches the Godot.NET.Sdk | | `ver.companions` | Avalonia and the other companion packages are not mixed | | `ver.tool-latest` | a newer 2dog tool on nuget.org is mentioned | ### Presets | Id | Passes when | | --- | --- | | `preset.file` | export_presets.cfg exists | | `preset.web` | the 'Web' preset exists when a browser host does | | `preset.android` | the Android hosts' export preset ('Android' by default) exists, unless they ship a pre-exported pck | | `preset.desktop` | the per-OS desktop presets exist | ### Godot settings | Id | Passes when | | --- | --- | | `godot.features-line` | config/features names the Godot line the SDK targets | | `godot.xr-shaders` | a WebXR host has xr/shaders/enabled.web set | | `godot.import-stamp` | the project has been imported at least once | ## Build-log signatures Failures `--build` and `--log` recognize. A match gets a title, the offending line and the fixing command; other errors follow. | Id | Failure | | --- | --- | | `build.variant-invalid` | TwoDogVariant is not release, debug or editor | | `build.buildtype-deprecated` | the deprecated TwoDogBuildType property is set | | `build.publish-aot` | PublishAot does not support the editor variant | | `build.publish-singlefile` | PublishSingleFile is not supported for desktop hosts | | `build.godot-sdk-mismatch` | Godot.NET.Sdk and 2dog.engine are on different Godot lines | | `build.no-import-capability` | no import capability (`2dog..editor` and 2dog.tools packages missing) | | `build.import-required` | the build requires an import (TwoDogRequireImport=true) and has no import capability | | `build.desktop-preset-missing` | the desktop export preset is missing from export_presets.cfg | | `build.web-preset-missing` | the 'Web' export preset is missing from export_presets.cfg | | `build.no-export-capability` | no export capability (`2dog..editor` and 2dog.tools packages missing) | | `build.web-payload-missing` | the browser natives (2dog.browser-wasm) are not restored | | `build.native-missing` | the native libgodot for this platform was not found | | `build.nu1213` | the '2dog' tool package is referenced as a library | | `build.package-not-found` | a package version is missing from the feeds | | `build.version-conflict` | conflicting package versions across the solution | | `build.msb3277-godotsharp` | two GodotSharp versions meet in one build | | `build.wasm-tools-missing` | the wasm-tools workload is not installed | | `build.android-workload-missing` | the android workload is not installed | | `build.sdk-too-old` | the installed .NET SDK is too old for net10.0 | | `build.global-json-unresolved` | no installed SDK satisfies global.json | | `build.godot-sdk-not-found` | the Godot.NET.Sdk version could not be downloaded | | `build.webboot-duplicate` | TwoDogWebBoot.cs compiles twice into the game assembly | | `build.il1035` | the trimmer cannot see a root assembly | | `build.export-failed` | the Godot pck export failed | | `build.godotplugins-missing` | GodotPlugins.dll not found at runtime | | `build.native-variant-missing` | the native library for this TwoDogVariant is missing at runtime | | `build.variant-fallback` | the engine fell back to another native variant | --- page: 2dog update section: Develop and Configure source: https://2dog.dev/cli/update.md description: Reference for 2dog update: bring a project's 2dog packages to the running tool's versions - arguments, options, examples. --- # `2dog update` Sets the project's 2dog package versions to the running tool's, and restores after project changes. Never downgrades. When Git can check the project, it refuses uncommitted tracked-file changes unless you pass `--allow-dirty`. ```bash 2dog update [path] [options] ``` | Argument | Meaning | | --- | --- | | `path` | Directory containing `project.godot`; defaults to the current directory | ## Options | Option | Effect | | --- | --- | | `--dry-run` | Print the plan; change nothing | | `--no-restore` | Skip the final `dotnet restore` | | `--allow-dirty` | Proceed despite uncommitted tracked-file changes | | `--install-wasm-tools` | Install missing `wasm-tools` for browser hosts | | `--install-android-workload` | Install the missing `android` workload for Android hosts | | `--update-workloads` | Install missing workloads the hosts need, then run `dotnet workload update` for the project's SDK | Plus the [global and output options](/dnx-2dog#global-options). There is no `--to`: pin the tool instead. For projects with browser or Android hosts, terminals offer to install a missing workload (`wasm-tools` or `android`), then, separately, to update the installed ones. `dotnet workload update` checks for and applies updates to **all installed workloads** for the selected SDK. `--no-restore` skips these offers; the explicit flags still apply. `--dry-run` never checks or changes workloads. ## Examples ```bash 2dog update # here, after committing 2dog update path/to/project --dry-run # show what would change dnx 2dog@4.7.2.106 update # a specific tool version ``` --- page: 2dog pack section: Develop and Configure source: https://2dog.dev/cli/pack.md description: Reference for 2dog pack list: list an exported .pck's contents by size, without an engine or project. --- # `2dog pack` File-format operations on Godot `.pck` bundles. Reads the pack directly: no engine, no project. ```bash 2dog pack list ``` `list` prints the pack format, the Godot version and every entry, largest first; `--json` puts the listing in the report. It answers "why is my pck 99 MiB?" and confirms an asset made it into a [web publish](/hosts/web). ```bash 2dog pack list MyGame.web/AppBundle/godot.pck ``` ```text MyGame.web/AppBundle/godot.pck: pack format v4 (Godot 4.7), 19 file(s), 0.0 MiB content 21,582 .godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex 11,593 icon.svg 7,602 .godot/exported/133200997/export-3070c538c03ee49b7677ff960a3f5195-main.scn 656 project.binary 316 spinning_cube.gdc ... ``` --- page: Pinning section: Develop and Configure source: https://2dog.dev/cli/pinning.md description: Choose which version of the 2dog tool to run, keep project package versions fixed, and understand what global.json controls. --- # Pinning a specific Version 2dog already pins your project to a specific version in `Directory.build.props`, which you can manually edit. You can also choose the version of the **2dog tool** you run and the version of the **2dog packages** the tool will use. These are separate settings. ## Run a specific tool version Add `@` and the version number after `2dog`: ```bash dnx 2dog@4.7.2.106 doctor ``` Replace the number with the version you want to keep using. This example runs `doctor` from that release of the tool. If it is not already downloaded, `dnx` downloads it. Use the same syntax with other commands. For example, this previews a project update using the selected tool version: ```bash dnx 2dog@4.7.2.106 update --dry-run ``` Remove `--dry-run` to apply the update. The update command changes the project's package versions to the tool's versions; it does not downgrade newer versions. See [update](/cli/update) for details. ## Print the version Use the word `version` to show the tool's version and its package versions: ```bash dnx 2dog@4.7.2.106 version ``` `--version` belongs to `dnx` and **chooses** the tool version. `version` is a 2dog command and **prints** version information. See the [version command reference](/cli/version) for the output format. ## Keep the project's package versions Projects created by 2dog store their package versions in `Directory.Build.props`. For example: ```xml 4.7.2.106 ``` This is a shortened example; keep the other version properties in your file. Projects use `$(TwoDogVersion)` in their package references. A version without `*` does not follow each new release. A normal build or restore does not rewrite this value. Running `2dog update` can change it. This property does not select the tool that `dnx` runs. Use `@version` for that. `global.json` selects SDK versions, not the 2dog tool or engine package version. ## Build without restoring packages If the project has already been restored and its packages are available locally, you can skip restore: ```bash dotnet run --project MyGame.2dog --no-restore ``` Replace `MyGame.2dog` with your host project. Choosing a version and skipping restore are different: a fixed version may still need downloading if it is missing. --- page: 2dog version section: Develop and Configure source: https://2dog.dev/cli/version.md description: Reference for 2dog version: print the tool version and every package a scaffold references, checked against nuget.org. --- # `2dog version` Prints the tool version and every package a scaffold references, each checked against nuget.org (best effort, 2.5 s). `--version` is the same, except [under dnx](/cli/pinning#print-the-version). ```bash 2dog version ``` ```text 2dog 4.7.2.106 https://2dog.dev tool + packages 4.7.2.106 ✅ 2dog, 2dog.engine, 2dog.avalonia, 2dog.blazor, 2dog.xunit native binaries 4.7.2.19 ✅ 2dog.win-x64, 2dog.linux-x64, 2dog.osx-arm64, 2dog.browser-wasm, 2dog.android, 2dog.android-arm64, 2dog.android-x64, 2dog.tools GodotSharp 4.7.2.106 ✅ 2dog.godotsharp, 2dog.godotsharp.editor ``` | Mark | Meaning | | --- | --- | | ✅ (`ok` when redirected) | The latest stable release | | 🔄 (`new`) | A newer stable release exists | | none | nuget.org could not be reached | --- page: 2dog help section: Develop and Configure source: https://2dog.dev/cli/help.md description: Reference for 2dog help: show the tool's usage, or the help for one verb. --- # `2dog help` Shows the usage, or the help for one verb. `2dog --help` is the same; `2dog` alone prints the version info and the usage. ```bash 2dog help [verb] ``` ```bash 2dog help new 2dog add --help ``` --- page: Resource Import section: Develop and Configure source: https://2dog.dev/import-tool.md description: How 2dog imports Godot resources automatically during MSBuild: incremental imports driven by GodotProjectDir, the twodog.import helper, and troubleshooting. --- # Resource Import Godot must import resources before a project can run. This generates C# script `.uid` files, processes assets, and builds the `.godot/` cache. Without it, the engine reports missing UIDs. 2dog handles the import **automatically during build**. ## Automatic import (MSBuild) Any project that sets `` and references `2dog.engine` gets an incremental import after `Build`: - Changed project files trigger an import. Otherwise, a shared stamp in `/.godot/` marks the project up to date. - `.gdignore` directories are not tracked, so editing a nested 2dog host does not trigger an import. Use `TwoDogImportExcludes` for more exclusions. - No installed Godot editor is required. A helper process uses the editor-variant libgodot from `2dog..editor`. ### Properties | Property | Default | Description | |----------|---------|-------------| | `TwoDogAutoImport` | `true` | Set `false` to disable the automatic import. | | `TwoDogRequireImport` | `false` | Set `true` to fail the build when no import capability is available (instead of a warning). | | `TwoDogForceImport` | `false` | Set `true` to force the import to run even when up-to-date. Also covers staleness the file tracking cannot see (deleted assets). | | `TwoDogImportStampFile` | `/.godot/2dog.import.stamp` | Override the stamp file location. | | `TwoDogImportExcludes` | - | Extra semicolon-separated glob excludes for the import inputs (directories with a `.gdignore` are always excluded). | | `GodotEditor` | - | Path to an external Godot editor binary. When set (or the `GODOT_EDITOR` environment variable is set), it is used instead of the in-process helper. | When neither the helper payload nor an external editor can be resolved, the build warns and skips the import. Set `TwoDogRequireImport` to fail instead. Deleting `.godot/` forces a full import on the next build. ## The import helper (`twodog.import`) `2dog.engine` includes `tools/net10.0/2dog.import.dll`, which MSBuild runs in a separate process. A `.godot/2dog.import.lock` serializes concurrent imports of the same project. ::: info Template builds (`template_debug`, `template_release`) cannot import; the pipeline requires the editor variant (`TOOLS_ENABLED`). ::: ## Troubleshooting ### Warning: no import capability was found The editor libgodot (`2dog..editor`), helper, or GodotTools assemblies (`2dog.tools`) could not be resolved. Restore `2dog.engine`, which depends on all three, or set `` to an external editor binary. ### GodotTools.dll not found The bundled import needs the GodotTools assemblies from `2dog.tools`. Restore `2dog.engine` normally, or configure an external editor with `` or `GODOT_EDITOR`. ### No .uid files generated Check for the `TwoDog: Imported Godot project` build message and confirm an *editor* libgodot or editor binary was used. Template builds cannot import. --- page: Testing with xUnit section: Develop and Configure source: https://2dog.dev/testing.md description: Run xUnit tests against a real Godot engine with 2dog.xunit: installation, rendering and headless fixtures, shared collections, and writing and running tests. --- # Testing with xUnit `2dog.engine` provides the fixtures in `twodog.Testing`. `2dog.xunit` adds ready-made xUnit collections, so tests can start a real Godot engine without having to manage its lifetime themselves. ## Installation ```bash dotnet add package 2dog.xunit dotnet add package xunit.v3 dotnet add package Microsoft.NET.Test.Sdk dotnet add package xunit.runner.visualstudio ``` `2dog.xunit` brings in `2dog.engine` automatically. To generate a test host in your Godot project, run `dnx 2dog add --tests`. Run the tests with: ```bash dotnet test MyGame.tests ``` The generated test project sits inside the Godot project, includes a `.gdignore`, and points `` at `..`. ## Fixtures Both fixtures derive from `FixtureBase`, which starts the engine in its constructor and exposes the objects most tests need: ```csharp public abstract class FixtureBase : IDisposable { protected FixtureBase(params string[] cmdLineArgs); public Engine Engine { get; } public GodotInstance GodotInstance { get; } public SceneTree Tree { get; } } ``` - `Fixture` starts Godot with rendering enabled. - `HeadlessFixture` adds `--headless` and is the usual choice for CI. ## Collections A Godot instance is not thread-safe. Put engine tests in a collection with `DisableParallelization = true`; tests in that collection share one fixture. `2dog.xunit` ships `RenderingCollection` and `HeadlessCollection`. Their collection definitions are compiled into your test assembly because xUnit does not discover definitions from an ordinary referenced DLL. ```csharp using twodog.Testing; using twodog.Testing.Xunit; [Collection] public class MyTests(HeadlessFixture godot) { // Tests share godot.Engine, godot.GodotInstance, and godot.Tree. } ``` You may use several non-parallel Godot collections. xUnit disposes one collection fixture before starting the next, giving each collection a fresh engine instance. ### Custom Collections For different Godot arguments, derive from `FixtureBase` and define the collection in your test project: ```csharp using twodog.Testing; using Xunit; public class OpenGl3Fixture() : FixtureBase("--rendering-driver", "opengl3"); [CollectionDefinition(nameof(OpenGl3Collection), DisableParallelization = true)] public class OpenGl3Collection : ICollectionFixture; ``` See [Single Godot Instance](/known-issues/single-instance) for the engine lifetime constraint. ## Writing Tests ```csharp using Godot; using twodog.Testing; using twodog.Testing.Xunit; using Xunit; [Collection] public class SceneTests(HeadlessFixture godot) { [Fact] public void LoadScene_ValidPath_Succeeds() { var scene = GD.Load("res://test_scene.tscn"); Assert.NotNull(scene); var instance = scene.Instantiate(); godot.Tree.Root.AddChild(instance); Assert.True(instance.IsInsideTree()); instance.QueueFree(); } } ``` ::: warning Godot types in MemberData Godot types such as `NodePath` and `StringName` can crash the runner during discovery. Pass primitive values or set `DisableDiscoveryEnumeration = true`. See [xUnit Test Discovery](/known-issues/xunit-discovery). ::: ## Godot Errors Any error or warning Godot reports while a test runs fails that test. That covers `push_error`, failed engine checks, and exceptions Godot catches in C# callbacks, which it would otherwise only print. A test that expects an error consumes it through the fixture, which also checks its text: ```csharp [Fact] public void RejectsNegativeHealth() { player.Health = -1; godot.Errors.Expect("Health must not be negative"); } ``` Errors reported between tests, such as during engine startup or from deferred calls an earlier test left queued, fail the next test on a fixture and say so. Tests without a 2dog fixture are not checked. To opt out, mark a test or class `[AllowGodotErrors]`, or set `false` in the test project. ## Godot's Thread Each collection that uses a 2dog fixture runs on a thread of its own. The engine starts there, and the tests, their `await` continuations, and the fixture's disposal all stay on it, as Godot requires. Between tests the thread also runs continuations Godot queued for its frame loop. On Windows the thread is STA, which Godot's windowing needs for drag-and-drop. `2dog.xunit` also gives test projects the comctl32 v6 manifest that `godot.exe` has, unless they set their own `ApplicationManifest`. On macOS that thread is the process main thread, because AppKit aborts a windowed engine started anywhere else. `2dog.xunit` wraps the entry point xUnit generates so that xUnit's runner moves to another thread and the main thread runs the 2dog collections, one at a time. A test project with its own entry point should run xUnit through `GodotTestThread.RunMain`. Tests without a 2dog fixture keep xUnit's usual threads. To use a different xUnit test framework, set `false`. That leaves out the entry point too, so on macOS windowed fixtures abort unless your framework runs their collections through `GodotTestThread.Run`, from an entry point that calls `GodotTestThread.RunMain`. ## Running Tests ```bash dotnet test dotnet test -c Release # or Debug / Editor dotnet test --logger "console;verbosity=detailed" dotnet test --filter "FullyQualifiedName~SceneTests" ``` Each configuration selects the matching engine variant; see [Build Variants](./build-configurations). Asset import runs automatically when the test project builds and does not require the Editor configuration; see [Resource Import](./import-tool). For headless CI, select the headless collection and use a dummy audio driver: ```yaml - name: Run tests run: dotnet test --configuration Release env: GODOT_AUDIO_DRIVER: Dummy ``` --- page: Build Variants section: Develop and Configure source: https://2dog.dev/build-configurations.md description: Choosing between the debug, release, and editor native Godot variants in 2dog, and how the Debug, Release, and Editor .NET configurations map onto them. --- # Build Variants 2dog selects the native Godot variant from your .NET build configuration. | .NET configuration | Default variant | Godot build | `TOOLS_ENABLED` | Use | | --- | --- | --- | --- | --- | | **Debug** | `debug` | `template_debug` | No | Development, debugging, tests | | **Release** | `release` | `template_release` | No | Production and final validation | | **Editor** | `editor` | `editor` | Yes | Editor types and `[Tool]` scripts | `debug` includes assertions and debugging support; `release` is the optimized production runtime; `editor` enables `TOOLS_ENABLED`, including editor types, import plugins, and `[Tool]` scripts. ::: warning Editor runtime limitations The variant provides compile-time access to editor APIs, but embedded libgodot does not initialize editor runtime singletons such as `EditorInterface`. Resource import runs separately and automatically at build time; see [Resource Import](./import-tool). ::: ## Selecting a Variant `TwoDogVariant` defaults to `debug` in Debug, `editor` in Editor, and `release` for Release and other configurations. Set it explicitly to override that mapping: ```xml Exe net10.0 debug ``` All three desktop variants come through the platform meta package, so selecting a variant needs no extra package references. `TwoDogVariant` also controls the copied native library, GodotPlugins layout, and embedded runtime metadata. Web hosts use `TwoDogWebVariant` with the same configuration mapping. The `2dog.browser-wasm` package restores both debug and release natives; a missing selected payload fails the build. Web and Android do not support the editor variant: use Debug or Release, or explicitly select a supported variant. Without an explicit variant override, use the configuration in the usual way: ```bash dotnet run -c Debug dotnet publish -c Release dotnet test -c Editor ``` If Godot reports that it cannot find its .NET assemblies, confirm that `TwoDogVariant` is `release`, `debug`, or `editor`, then clean and rebuild: ```bash dotnet clean dotnet build -c Debug ``` See [Configuration](./configuration) for the property and package reference. --- page: MSBuild Configuration section: MSBuild Configuration source: https://2dog.dev/configuration.md description: Shared 2dog settings, MSBuild runtimes, package versions, and configuration references for each host. --- # MSBuild Configuration Set host properties inside a `` in its `.csproj`. Put shared settings in `Directory.Build.props`. Project settings override shared values; command-line properties (`-p:Name=value`) override project settings. Omitted 2dog properties use their documented defaults. ## Global Properties {#properties} | Property | Default | Purpose | | --- | --- | --- | | `GodotProjectDir` | None | Directory containing `project.godot`; enables resource import and is embedded for `Engine.ResolveProjectDir()` | | `TwoDogVariant` | `debug` in Debug, `editor` in Editor, `release` otherwise | Desktop/Android native variant; explicit settings override the default. Android does not support `editor`; browser hosts use `TwoDogWebVariant` | | `TwoDogRemoveDuplicateGodotAnalyzers` | `false` | Removes duplicate analyzers from a host that also references a `Godot.NET.Sdk` game project | For a host nested directly inside the Godot project: ```xml .. true ``` `GodotProjectDir` is resolved relative to the project file and embedded as an absolute path. The game project keeps its Godot source generator; only the host should remove duplicate analyzers. Blazor clients are nested an extra level and normally use `../..`. See [Build Variants](./build-configurations) for native selection and [Resource Import](./import-tool) for import controls, including `TwoDogAutoImport`, `TwoDogRequireImport`, and `GodotEditor`. ## Packages and Versions Package versions begin with the embedded Godot version. Pin manual references to your project's Godot line, for example `Version="4.7.2.*"`, so NuGet does not silently select a newer engine line. Scaffolded projects keep versions in one block of the root `Directory.Build.props`: ```xml 4.7.2.106 4.7.2.19 4.7.2 ``` Hosts reference those properties: ```xml ``` [`2dog update`](/cli/update) rewrites the version block and the game project's `Godot.NET.Sdk` version, which cannot come from a property. [`2dog doctor`](/cli/doctor) reports remaining literal versions and mismatched Godot lines. `2dog.engine` references the desktop meta packages `2dog.win-x64`, `2dog.linux-x64`, and `2dog.osx-arm64`. Their targets select the native for the build or publish platform. Each pins the release, debug, and editor packages; the selected library is copied as `libgodot-.dll`, `.so`, or `.dylib`. `2dog.browser-wasm` pins both release and debug browser natives. ## Native AOT Generic, Avalonia, and WinUI hosts support `PublishAot=true` with debug or release natives. The editor variant loads GodotTools at runtime and cannot use Native AOT. WinForms does not support trimming or Native AOT. Desktop Native AOT requires the platform's native toolchain: Visual Studio C++ build tools on Windows, `clang` and `zlib1g-dev` on Linux, or Xcode command-line tools on macOS. Publish on the operating system you target; see the [.NET prerequisites](https://learn.microsoft.com/dotnet/core/deploying/native-aot/#prerequisites). 2dog preserves `GodotSharp` and the game assembly for reflection. Root other assemblies that are only accessed through reflection: ```xml ``` Android has [experimental Native AOT](./configuration/android#native-aot) with NDK requirements and different trimmer roots. Browser hosts use Mono's AOT compiler through `RunAOTCompilation`, described in their host references. ## MSBuild Runtimes `Core`, `Full`, and `Mono` describe the runtime executing MSBuild. They are independent of your project's `TargetFramework`, build `Configuration`, and the runtime used by your published app. Windows builds can use Core or Full. | `MSBuildRuntimeType` | Build tool | | --- | --- | | `Core` | MSBuild from the .NET SDK: `dotnet build`, `dotnet publish`, or `dotnet msbuild` | | `Full` | .NET Framework MSBuild, such as Visual Studio's `MSBuild.exe` | | `Mono` | Legacy MSBuild running on Mono | MSBuild sets this [reserved property](https://learn.microsoft.com/visualstudio/msbuild/msbuild-reserved-and-well-known-properties#reserved-and-well-known-properties) automatically. To inspect the runtime used by a command: ```bash dotnet msbuild MyGame.2dog/MyGame.2dog.csproj -getProperty:MSBuildRuntimeType ``` Use the .NET 10+ SDK for 2dog's CLI workflows. Currently, Web and WebXR [precompression](./configuration/web#precompression) runs only under Core MSBuild; Full and Mono skip it even when explicitly enabled. The current inline task depends on Core's compression APIs. --- page: Generic .NET Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/generic.md description: MSBuild settings for the generic .NET host: console behavior, desktop pack export, and Native AOT. --- # Generic .NET Configuration Set properties in `MyGame.2dog.csproj`, which references `2dog.engine`. The [global settings](/configuration#properties) control the game directory, resource import, analyzers, and native variant. ## Publishing | Property | Default | Purpose | | --- | --- | --- | | `TwoDogExportPack` | `true` | Export content to an executable-adjacent `.pck`; `false` skips export, so supply game content separately | | `TwoDogDesktopExportPreset` | RID-mapped | Preset in `export_presets.cfg`: `Windows Desktop`, `Linux`, or `macOS`, according to the publish target | | `PublishSelfContained` | `true` in the generated host | Bundle the .NET runtime; `false` requires an installed runtime | | `PublishAot` | `false` | Compile the host and managed game code into a native executable | ```bash dotnet publish MyGame.2dog -c Release -r win-x64 -p:PublishAot=true ``` Native AOT supports debug and release variants; see [requirements and trimmer roots](/configuration#native-aot). `PublishSingleFile` is unsupported for ordinary desktop publishing; use Native AOT when you need a single native executable. ## Console Behavior The generated host uses `OutputType=WinExe` in Release to hide the Windows console, and `Exe` otherwise. Set `OutputType=Exe` to keep console output in Release. Linux and macOS treat `WinExe` as `Exe`. See the [generic host guide](/hosts/generic) for running the host and passing Godot arguments. --- page: Android Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/android.md description: MSBuild configuration for experimental Android hosts: native variants, game packs, signing, trimming, and Native AOT. --- # Android Configuration Set properties in `MyGame.android.csproj`. The generated host references `2dog.engine`, `2dog.android`, and the native meta package for its selected `android-arm64` or `android-x64` RID. See [global settings](/configuration#properties) for the game directory and import controls. ## Properties | Property | Default | Purpose | | --- | --- | --- | | `TwoDogVariant` | `debug` in Debug, `editor` in Editor, `release` otherwise | Native engine variant; explicit settings override the default. The `editor` variant is unsupported | | `TwoDogAndroidGameAssembly` | Set by the generated host | Game assembly that Godot loads; kept whole by the trimmer | | `TwoDogAndroidExportPreset` | `Android` | Preset in `export_presets.cfg` | | `TwoDogAndroidPack` | Exported by the build | Use an existing game pack instead of exporting one | | `TwoDogAndroidGdExtensions` | `true` | Package the Android libraries of the project's GDExtensions | | `TwoDogAndroidSigning` | `true` | Sign using .NET signing properties, Godot's keystore variables, or the debug keystore | | `AndroidPackageFormats` | `apk` in the generated host | `apk` for sideloading, `aab` for app stores; .NET for Android otherwise defaults to `aab` in Release | Android exports its pack during the build and puts it inside the APK/AAB. `TwoDogExportPack` is disabled by the Android targets because the desktop publish export is not used; supply an existing pack through `TwoDogAndroidPack`. See the [Android host guide](/hosts/android#build-and-install) for SDK setup, installation, and keystore precedence. ## Trimming and Mono AOT Release builds are trimmed and AOT-compiled by default (`TrimMode=partial`, profiled AOT). 2dog preserves `GodotSharp`, `GodotPlugins`, `twodog`, and the game assembly. `TrimMode=full` and `AndroidEnableProfiledAot=false` (AOT for every method) also work. Root other assemblies accessed only through reflection: ```xml ``` ## Native AOT .NET for Android's Native AOT is experimental (warning XA1040). It supports 2dog's debug and release variants, links with the Android NDK, and can build on any supported build machine. Install the NDK and set `AndroidNdkDirectory`: ```bash dotnet publish MyGame.android -c Release -p:PublishAot=true -p:AndroidNdkDirectory=$ANDROID_HOME/ndk/29.0.14206865 ``` Without `PublishAot`, Release builds use Mono's AOT compiler through `RunAOTCompilation`. --- page: Avalonia Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/avalonia.md description: MSBuild settings for Avalonia hosts: shared engine settings, desktop pack export, and Native AOT. --- # Avalonia Configuration Set properties in `MyGame.avalonia.csproj`. `2dog.avalonia` brings in `2dog.engine`; [global settings](/configuration#properties) control the game directory, resource import, analyzers, and native variant. | Property | Default | Purpose | | --- | --- | --- | | `TwoDogExportPack` | `true` | Export content to an executable-adjacent `.pck`; `false` skips export, so supply game content separately | | `TwoDogDesktopExportPreset` | RID-mapped | `Windows Desktop`, `Linux`, or `macOS`, according to the publish target | | `PublishSelfContained` | `true` in the generated host | Bundle the .NET runtime; `false` requires an installed runtime | | `PublishAot` | `false` | Compile the host and managed game code into a native executable | ```bash dotnet publish MyGame.avalonia -c Release -r win-x64 -p:PublishAot=true ``` Native AOT supports debug and release variants; see [requirements and trimmer roots](/configuration#native-aot). The generated host uses `OutputType=WinExe`; choose `Exe` to see the engine's console diagnostics on Windows. Keep its app manifest for correct DPI scaling. See the [Avalonia host guide](/hosts/avalonia) for UI setup and platform requirements. --- page: Blazor Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/blazor.md description: MSBuild configuration for the Blazor WebAssembly client: game packs, native symbols, memory, and SDK-managed compression. --- # Blazor Configuration Set 2dog properties in `MyGame.blazor/Client/MyGame.blazor.Client.csproj`. The client references `2dog.engine`, `2dog.blazor`, and `2dog.browser-wasm`. It normally sets `GodotProjectDir=../..`; see the [global settings](/configuration#properties). ## Client Properties | Property | Default | Purpose | | --- | --- | --- | | `TwoDogWebVariant` | `debug` in Debug, `editor` in Editor, `release` otherwise | Native Web engine; explicit settings override the default. The `editor` variant is unsupported | | `TwoDogExportPack` | `true` | Export the game pack as a static web asset; `false` uses an existing `wwwroot/godot.pck` in the client | | `TwoDogWebExportPreset` | `Web` | Preset in `export_presets.cfg` | | `TwoDogWebPackName` | `godot.pck` | Exported pack name; match it in `GodotView.PackUrl` if changed | | `TwoDogWebSideModuleExports` | `true` | Export native symbols used by GDExtensions | | `WasmEmitSymbolMap` | `false` in Release, `true` otherwise | Include native symbols for stack traces; explicit settings override the default | | `WasmInitialHeapSize` | `256MB` | Initial memory allocation; memory can grow | `2dog.browser-wasm` restores both supported native variants; a missing selected payload fails the build. The client SDK manages its own bundle, progress, source maps, and compression. `TwoDogWebSizeManifest`, `TwoDogWebStripMaps`, and `TwoDogWebPrecompress` are settings for Web/WebXR hosts. ## Publishing, Trimming, and AOT Publish the server project to include its client: ```bash dotnet publish MyGame.blazor -c Release -p:RunAOTCompilation=true ``` `RunAOTCompilation` enables Mono's browser AOT compiler. The generated client preserves the game, host, `GodotSharp`, and `twodog` assemblies. Add `` in an `` for other assemblies accessed only through reflection. Keep `PrivateAssets="all"` on the client's engine and browser package references so their build targets stay out of the server; the generated engine reference also sets `Publish="true"` so its managed assemblies remain in the client publish. See the [Blazor host guide](/hosts/blazor) for `GodotView` and static asset serving. --- page: Web Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/web.md description: MSBuild properties for browser-wasm hosts: native variants, game packs, symbols, compression, and Mono AOT. --- # Web Configuration Set properties in `MyGame.web.csproj`, which references `2dog.engine` and `2dog.browser-wasm`. The generated host uses `RuntimeIdentifier=browser-wasm` and defaults to Release; pass `-c Debug` for a Debug build. See [global settings](/configuration#properties) for the game directory and import controls. ## Properties | Property | Default | Purpose | | --- | --- | --- | | `TwoDogWebVariant` | `debug` in Debug, `editor` in Editor, `release` otherwise | Native Web engine; explicit settings override the default. The `editor` variant is unsupported | | `TwoDogExportPack` | `true` | Export game content; `false` uses an existing `wwwroot/godot.pck` | | `TwoDogWebExportPreset` | `Web` | Preset in `export_presets.cfg` | | `TwoDogWebPackName` | `godot.pck` | Deployed pack name | | `TwoDogWebSizeManifest` | `true` | Write `twodog.sizes.json` for loading progress | | `TwoDogWebStripMaps` | `true` with the release engine | Remove JavaScript source maps | | `TwoDogWebPrecompress` | `true` with `dotnet publish` | Write Brotli (`.br`) and gzip (`.gz`) copies of large bundle files | | `TwoDogWebPrecompressLevel` | `Optimal` | `Optimal` (balance speed and size), `Fastest`, `SmallestSize` (slower), or `NoCompression`; case-insensitive | | `TwoDogWebSideModuleExports` | `true` | Export native symbols used by GDExtensions | | `WasmEmitSymbolMap` | `false` in Release, `true` otherwise | Include native symbols for stack traces; explicit settings override the default | | `WasmInitialHeapSize` | `256MB` | Initial memory allocation; memory can grow | `2dog.browser-wasm` restores both debug and release natives. A missing selected payload fails the build. Editor configuration selects the unsupported editor variant and fails; use Debug or Release, or explicitly select a supported variant. Changing `TwoDogWebPackName` also requires matching the pack URL in your boot page. ## Precompression `dotnet publish` enables precompression; set `TwoDogWebPrecompress=false` to skip it. Originals are kept. Currently, Full and Mono MSBuild skip this step even when explicitly enabled; use `dotnet publish` to generate compressed copies. See [MSBuild runtimes](/configuration#msbuild-runtimes). `NoCompression` still creates `.br` and `.gz` files. Use `TwoDogWebPrecompress=false` to skip generating them. ## Trimming and AOT The generated host preserves the game, host, `GodotSharp`, and `twodog` assemblies. Root other assemblies accessed only through reflection: ```xml ``` Browser hosts run on Mono. Enable Mono's AOT compiler with `-p:RunAOTCompilation=true`; use this instead of `PublishAot`. See the [Web host guide](/hosts/web) for serving and deploying `AppBundle/`. --- page: WebXR Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/webxr.md description: MSBuild configuration for WebXR hosts, using the Web host's browser engine, pack, compression, and AOT settings. --- # WebXR Configuration Set properties in `MyGame.webxr.csproj`. WebXR uses the same [browser build properties](./web#properties), [precompression](./web#precompression), and [trimming and Mono AOT settings](./web#trimming-and-aot) as the Web host. The generated host defaults to Release. `TwoDogWebVariant` selects debug for Debug and release for Release. Editor is unsupported. `TwoDogWebExportPreset` defaults to `Web`, and `TwoDogExportPack=false` uses an existing `wwwroot/godot.pck`. `WasmEmitSymbolMap` defaults to `false` in Release and `true` otherwise. ```bash dotnet publish MyGame.webxr -c Release -p:RunAOTCompilation=true ``` There are no additional WebXR-specific MSBuild properties. XR shaders and scene setup belong to the Godot project; see the [WebXR host guide](/hosts/webxr). --- page: WinForms Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/winforms.md description: MSBuild settings for WinForms hosts: Windows targeting, pack export, runtime bundling, and trimming limitations. --- # WinForms Configuration Set properties in `MyGame.winforms.csproj`, which references `2dog.engine`. The generated host targets `net10.0-windows` with `UseWindowsForms=true`. [Global settings](/configuration#properties) control the game directory, resource import, analyzers, and native variant. | Property | Default | Purpose | | --- | --- | --- | | `TwoDogExportPack` | `true` | Export content to an executable-adjacent `.pck`; `false` skips export, so supply game content separately | | `TwoDogDesktopExportPreset` | `Windows Desktop` | Preset in `export_presets.cfg` for the Windows game pack | | `PublishSelfContained` | `true` in the generated host | Bundle the .NET runtime; `false` requires an installed runtime | ```bash dotnet publish MyGame.winforms -c Release -r win-x64 ``` WinForms does not support trimming or Native AOT. Keep the generated `ApplicationHighDpiMode=SystemAware` and app manifest so WinForms and Godot use the same DPI mode. The host uses `OutputType=WinExe`; choose `Exe` to see console diagnostics. See the [WinForms host guide](/hosts/winforms) for window embedding and UI behavior. --- page: WinUI 3 Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/winui.md description: MSBuild settings for WinUI 3 hosts: desktop packs, self-contained Windows App SDK deployment, and Native AOT. --- # WinUI 3 Configuration Set properties in `MyGame.winui.csproj`, which references `2dog.engine` and `Microsoft.WindowsAppSDK`. Build and publish on Windows. [Global settings](/configuration#properties) control the game directory, resource import, analyzers, and native variant. | Property | Default | Purpose | | --- | --- | --- | | `TwoDogExportPack` | `true` | Export content to an executable-adjacent `.pck`; `false` skips export, so supply game content separately | | `TwoDogDesktopExportPreset` | `Windows Desktop` | Preset in `export_presets.cfg` for the Windows game pack | | `PublishSelfContained` | `true` in the generated host | Bundle the .NET runtime; `false` requires an installed runtime | | `PublishAot` | `false` | Compile the host and managed game code into a native executable | The generated host sets `RuntimeIdentifier=win-x64`, `WindowsPackageType=None`, and `WindowsAppSDKSelfContained=true` for an unpackaged app carrying its Windows App SDK runtime. That runtime setting is separate from .NET's `PublishSelfContained`. ```bash dotnet publish MyGame.winui -c Release -p:PublishAot=true ``` Native AOT supports debug and release variants; see [requirements and trimmer roots](/configuration#native-aot). The host uses `OutputType=WinExe`; choose `Exe` to see console diagnostics. See the [WinUI host guide](/hosts/winui) for window embedding and UI behavior. --- page: xUnit Configuration section: MSBuild Configuration source: https://2dog.dev/configuration/xunit.md description: MSBuild configuration for xUnit hosts: engine variants, resource import, and Editor API references. --- # xUnit Configuration Set properties in `MyGame.tests.csproj`. Reference `2dog.xunit`, which brings in `2dog.engine` and the compile-in collection definitions. The [global settings](/configuration#properties) control the game directory, resource import, analyzers, and native variant. | Configuration | Default native | Use | | --- | --- | --- | | Debug | `debug` | Development tests with assertions | | Release | `release` | Tests against the production engine | | Editor | `editor` | Tests using Editor APIs and `[Tool]` scripts | ```bash dotnet test MyGame.tests -c Editor ``` The generated Editor configuration also defines `EDITOR` and references `2dog.godotsharp.editor`. A manually created test project needs that managed reference to compile Editor API calls; selecting the editor native alone does not add it. Tests use the game directory and its imported resource cache. See [Resource Import](/import-tool) to require or force an import, and [Testing with xUnit](/testing) for fixture and runner settings. --- page: Known Issues section: Known Issues source: https://2dog.dev/known-issues/index.md description: Current 2dog limitations and their workarounds: one Godot instance per load context, xUnit discovery crashes with Godot types, GD.Print visibility in tests, and spaced project names breaking publish. --- # Known Issues 2dog is early-stage software. These pages track current limitations and their workarounds. ## Current Issues - **[Single Godot Instance](./single-instance)**: one instance per assembly load context at a time; sequential restart is supported and isolated concurrent hosting is experimental - **[xUnit Test Discovery](./xunit-discovery)**: Godot types in `[MemberData]` crash discovery because the runner cannot resolve them then - **[GD.Print in Tests](./gd-print-output)**: `GD.Print` output is hidden by default - **[Spaced Project Names](./spaced-project-names)**: whitespace in the game's .NET name makes publish silently drop its NuGet dependencies (.NET SDK bug); `2dog add --rename` fixes it If you encounter issues not listed here, please [open an issue on GitHub](https://github.com/outfox/2dog/issues). --- page: Single Godot Instance section: Known Issues source: https://2dog.dev/known-issues/single-instance.md description: Only one Godot instance may run per assembly load context in 2dog; sequential restart is supported and isolated concurrent hosting is experimental. --- # Single Godot Instance Per Load Context Only one Godot instance may run in an assembly load context at a time. Starting a second instance throws `InvalidOperationException`: ```csharp using var engine1 = new Engine("MyGame"); engine1.Start(); using var engine2 = new Engine("MyGame"); engine2.Start(); // Throws InvalidOperationException. ``` Sequential restart is supported in packages based on Godot 4.7 and later, including the browser: the [Blazor host](/hosts/blazor) restarts on a fresh canvas, and web hosts keep the runtime alive by default (`Engine.WebExitRuntimeOnQuit = false`). Any host can start a new engine after `Completion` completes and `Exited` fires. `Engine` owns its instance; the `GodotInstance` returned by `Start()` is a borrowed compatibility handle. On desktop, dispose the current engine before creating another: ```csharp using (var engine = new Engine("MyGame")) { engine.Start(); engine.Run(); } // Synchronous teardown. using var nextEngine = new Engine("MyGame"); nextEngine.Start(); ``` In a browser, call `RequestQuit()` while `Run()` pumps frames and await `Completion`, or call `DisposeAsync()` before creating the new `Engine`; browser teardown is asynchronous. This allows xUnit collections to use fresh engines sequentially in one test process. Collections that share an engine must disable parallelization; see [Testing](../testing). Shutdown releases every Godot object the engine created before it returns, including wrappers the garbage collector dropped but had not finalized yet, so nothing from one engine is released into the next. You do not need to force a garbage collection between engines. A restart does not reload the game assembly: static fields keep their values across engines, while every Godot object the previous engine created was disposed when it shut down. A static that holds a Godot object - a `Resource` such as `FastNoiseLite`, a `Node`, a `PackedScene` - therefore throws `ObjectDisposedException` in the next engine. Create such objects per instance (in `_Ready`, or lazily behind `GodotObject.IsInstanceValid`), or reset them from `Exited`: ```csharp private static FastNoiseLite? _noise; private static FastNoiseLite Noise => GodotObject.IsInstanceValid(_noise) ? _noise : _noise = new FastNoiseLite(); ``` --- page: xUnit Test Discovery Crash section: Known Issues source: https://2dog.dev/known-issues/xunit-discovery.md description: Godot values in xUnit MemberData can crash the test host during discovery - the affected Godot types and the workarounds. --- # xUnit Test Discovery Crash with Godot Types Godot values in xUnit `[MemberData]` can crash the test host during discovery ([issue #16](https://github.com/outfox/2dog/issues/16)): ```csharp public static IEnumerable Paths = [[new NodePath("/root")]]; [Theory] [MemberData(nameof(Paths))] public void CanLogNodePath(NodePath path) { GD.Print(path); } ``` xUnit enumerates member data before fixtures run. Constructing types that call Godot native code at that point fails because the engine has not started. ## Workarounds ### Workaround 1: Disable Discovery Enumeration Defer member-data enumeration until test execution: ```csharp [Theory] [MemberData(nameof(Paths), DisableDiscoveryEnumeration = true)] public void CanLogNodePath(NodePath path) { GD.Print(path); } ``` The test explorer will show one theory entry rather than one entry per data row. ### Workaround 2: Use Primitive Values For separately discoverable rows, pass primitive values and create the Godot value inside the test after its fixture has started: ```csharp public static IEnumerable Paths = [["/root"], ["/root/Main"]]; [Theory] [MemberData(nameof(Paths))] public void CanLogNodePath(string value) { var path = new NodePath(value); GD.Print(path); } ``` Prefer this one. ## Affected Types Any GodotSharp type whose constructor calls native code is affected: `NodePath`, `StringName`, `Rid`, `GodotObject` subclasses, and some math value types. Primitive C# values such as strings, numbers, booleans, and their arrays are safe for discovery. --- page: GD.Print in Tests section: Known Issues source: https://2dog.dev/known-issues/gd-print-output.md description: Why GD.Print output is hidden during dotnet test, how to make it visible, and why ITestOutputHelper is the recommended pattern. --- # GD.Print Output Not Visible in Tests `GD.Print` prints nothing under `dotnet test`. The output exists; the runner hides it. ## Why This Happens `GD.Print` writes to stdout through Godot's native `OS::print()`. `dotnet test` hides test-host stdout by default. The stream is global, so output also mixes with engine and fixture logs rather than belonging to a specific test. ## Making GD.Print Visible Enable detailed console logging: ```bash dotnet test --logger "console;verbosity=detailed" ``` Your messages show up, mixed into the test-host stream with engine and fixture logs. ## Use ITestOutputHelper instead For anything a test wants to say, use xUnit's `ITestOutputHelper`: ```csharp [Collection] public class MyTests(HeadlessFixture godot, ITestOutputHelper output) { [Fact] public void MyTest() { var node = godot.Tree.Root; // Instead of: GD.Print(node.GetPath()); output.WriteLine(node.GetPath()); } } ``` The output then belongs to that test: it lands in the failure report and in the IDE test explorer, with no engine noise around it. `GD.Print` stays what it is, a runtime debugging aid. --- page: FAQ section: Optional source: https://2dog.dev/faq.md description: Frequently asked questions about 2dog: how it differs from godot-dotnet, GodotSharp compatibility, and separate tool and engine packages. --- # FAQ ## How is 2dog different from godot-dotnet? They solve opposite problems: - **[godot-dotnet](https://github.com/godotengine/godot-dotnet)** puts .NET in Godot through a new bindings layer for native extensions. - **2dog** puts Godot in .NET: your application hosts the engine as a library. 2dog uses GodotSharp; it changes the process structure: **Classic Godot with C# (godot-mono):** ``` godot-mono <- the engine process drives everything ├── loads GodotSharp │ └── runs your code └── loads GDExtensions ``` **2dog:** ``` your .NET application <- your process drives the engine └── loads libgodot ├── loads GodotSharp └── loads GDExtensions ``` ### When to use which Use godot-dotnet to write native extensions in .NET. Use 2dog to embed or package Godot in a .NET application, run tests with `dotnet test`, or add .NET testing and tooling around an existing game. ## Will 2dog use godot-dotnet in the future? Likely, when it becomes a practical GodotSharp replacement. As of mid-2026, godot-dotnet provides early plumbing but not GodotSharp's full capabilities, so 2dog remains on GodotSharp. ## Is 2dog a replacement for GodotSharp? No. 2dog embeds GodotSharp and Godot's C# source generators. `GD`, `Node`, `[Export]`, signals, and other C# APIs work as usual; only process ownership changes. ## Why is the library a separate package (`2dog.engine`) instead of part of `2dog`? NuGet packages marked as dotnet tools cannot also be consumed through a `PackageReference`; doing so fails with `NU1213`. Therefore `2dog.engine` is the library, while `2dog` contains the self-contained tool and template. They are released together, and both scaffolding routes produce the same output. See [`2dog add`](/cli/add). --- Have a question that isn't answered here? Ask on [Discord](https://discord.gg/GAXdbZCNGT) or [open an issue on GitHub](https://github.com/outfox/2dog/issues). --- page: Troubleshooting section: Optional source: https://2dog.dev/troubleshooting.md description: What the 2dog tool's exit codes, notes and errors mean, how it behaves in CI and pipes, and where to look when a restore or build fails. --- # Troubleshooting Most problems have a first step in common: run [`2dog doctor`](/cli/doctor) in the project. It checks the machine and the project, fixes what it safely can, and explains build failures it recognizes (`2dog doctor --build`, or `2dog doctor --log ` for a log you already have). ## Exit codes | Code | Meaning | What to do | | --- | --- | --- | | `1` | Usage error | The `error:` line names the problem and suggests the closest option or verb; `2dog --help` lists what the verb accepts | | `2` | Tool error | Project state (no `project.godot`, a name with spaces, invalid XML), a failed `dotnet` call, or a plan that stopped half-way. The `error:`/`hint:` lines say which; `--verbose` adds the stack trace and every subprocess line | | `3` | Doctor findings remain, or the `--build` failed | Read the report; `--fix` applies the safe fixes, `--ignore ` drops one you accept. After `--build`, the build section names the matched failure and the full log | | `130` | Cancelled | Ctrl+C during a prompt or a subprocess; nothing after the cancelled step ran | ## A plan stopped half-way `2dog new`, `add` and `update` apply their steps in order with no rollback. A failing step is reported as `error: step N/M failed: ...`, followed by a `note:` listing the steps that stand and how many did not run. Fix the cause (a read-only file, the Godot editor holding a csproj open) and run the same command again: existing files are skipped, `--force` overwrites. ## No terminal, CI, pipes With stdout not a terminal, or one of `CI`, `GITHUB_ACTIONS`, `TF_BUILD`, `GITLAB_CI`, `TEAMCITY_VERSION`, `BUILD_NUMBER` set, the tool never prompts. `new` and `add` without host flags then apply the default host set and print `note: no terminal to ask on`; pass `--yes` or name the hosts to make that explicit. `doctor` prints the fix commands instead of asking. Markers fall back to ASCII; `--json` gives one document on stdout; `--plain` and `NO_COLOR` turn styling off; `CLICOLOR_FORCE` keeps colour in a pipe. ## `dotnet restore` failed The tool prints the last lines of the restore and keeps going (the files are already written). Common causes: - **`NETSDK1147` / wasm-tools**: the browser hosts need the workload - `dotnet workload install wasm-tools`. - **`NU1101`/`NU1102`, a `2dog.*` package version not found**: the version the project pins was not published yet, or your `nuget.config` hides nuget.org. `2dog update` pins the versions the running tool ships. - **`NU1213`**: a project references the `2dog` tool package; reference `2dog.engine` instead. Run `dotnet restore` again after fixing the cause; `2dog doctor --log` explains a saved restore log the same way it explains builds. ## "dotnet not found" The tool runs `dotnet sln`, `dotnet restore` and (for doctor) `dotnet build` through the muxer that launched it (`DOTNET_HOST_PATH`), then `DOTNET_ROOT`, then `PATH`. Set `DOTNET_HOST_PATH` to the `dotnet` executable when none of those apply. ## Testing locally packed packages When you develop 2dog itself, same-version repacks are masked by the NuGet cache and by nuget.org. Pack with a prerelease suffix CI can never publish and point the consumer at the local feed: ```bash dotnet pack twodog.godotsharp -c Release -p:TwoDogVersion=4.7.2.106-local.1 dotnet pack twodog.godotsharp.editor -c Release -p:TwoDogVersion=4.7.2.106-local.1 dotnet pack twodog.engine -c Release -p:TwoDogVersion=4.7.2.106-local.1 python tests/bindings/test_packages.py --version 4.7.2.106-local.1 # consumer nuget.config: add the repo's packages/ folder as a source, reference -local.1 ``` ## Mixed Godot bindings (TDG001–TDG004) Run the current `2dog update` to configure fork bindings in an existing game. `2dog add` and new templates do this automatically too. No DisableImplicitGodotSharpReferences or DisableImplicitGodotGeneratorReferences properties are required. Compatible stock GodotSharp/GodotSharpEditor package assets are replaced with the matching fork assemblies before compile/copy/publish. TDG001 now means a dependency targets a different Godot version: align its version (or Godot.NET.Sdk) with the engine's Godot version. TDG003 identifies a stale manual assembly reference; remove the conflicting HintPath. TDG004 requires core/editor/engine packages to use the same 2dog version. The published 4.7.2.89 binding package rejected all mixed package graphs. Updating only the CLI cannot repair that package's build targets; upgrade the engine and managed packages together to a release containing the compatibility fix. Builds still validate binding contents before compilation and publish trimming. IL2125 warnings for your own libraries require a separate trimming review. ## The Godot editor holds a file `could not rename ...` or `the file is in use`: close the Godot editor and any IDE with the project open, then re-run. On Windows the editor keeps the game csproj and its build outputs locked while the project is open. --- page: Spaced Project Names section: Optional source: https://2dog.dev/known-issues/spaced-project-names.md description: Whitespace in the game project's .NET name makes dotnet publish silently drop its NuGet dependencies from 2dog hosts - an upstream .NET SDK bug 2dog guards against. --- # Project Names With Spaces Break Publish A .NET SDK bug makes `dotnet publish` silently drop a *referenced* project's transitive NuGet packages whenever that project's restore name contains whitespace and any reference in the publishing project carries `Publish=false`. Every 2dog web publish qualifies: `PublishTrimmed` brings in the implicit `Microsoft.NET.ILLink.Tasks` reference with `PrivateAssets="all"`. The symptom: the game's own DLL ships, but every NuGet package it references (a JSON library, a logging framework, ...) is missing from `AppBundle/_framework`, and the app dies at startup with: ```text System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=13.0.0.0, ...' ``` Godot sets this trap readily - a Godot project named "Fast Dragon" gets a generated `Fast Dragon.csproj` with a matching assembly name. Stock Godot never notices (its export publishes the game as the *root* project, which is unaffected); 2dog hosts reference the game, which is exactly the broken case. ## The mechanism NuGet records each project's direct dependencies in `project.assets.json` as flat strings (`"Fast Dragon >= 1.0.0"`), and the SDK's publish-exclusion computation parses the name by cutting at the first whitespace - yielding `Fast`, which matches nothing. The game's whole package subtree is then treated as unreachable and excluded from publish. The restore name resolves as `PackageId` → `AssemblyName` → project file name, so all of them must be space-free. ## What 2dog does `2dog add`/`convert` refuses to scaffold hosts against a name containing whitespace and offers to fix it - interactively, or via `2dog add --rename NewName`. The rename touches the .NET identity only: the csproj file, `[dotnet] project/assembly_name` in `project.godot`, and the solution reference. Godot's display name (`config/name`) keeps its spaces. `2dog new` never creates such names (spaces are stripped, with a notice). ## Fixing an affected project by hand 1. Close the Godot editor. 2. Rename `Fast Dragon.csproj` to `FastDragon.csproj`. 3. Set `project/assembly_name="FastDragon"` in `project.godot`'s `[dotnet]` section. 4. Point the solution's entry at the new csproj name. 5. In each existing host csproj, update the `ProjectReference`, any `TrimmerRootAssembly`, and the `RootNamespace` that carry the old name. Adding the game's packages directly to the host csproj also works around the bug (direct references are never excluded) - but it must be kept in sync with the game's packages by hand, so prefer the rename.