# Guest SDK: link Fusion into your game (v0)

For programs whose source you have (open-source games, Rust rewrites, recomps, your own game),
and for **your own plugin inside a closed game** (a BepInEx or MelonLoader plugin, an SKSE plugin,
a Source server plugin, an injected DLL). They link Fusion's protocol directly, so they need no
engine kit. The game's own code still runs every mechanic; the SDK only tells Fusion what the
game has and shows its picture. (Closed games on Unreal or Unity IL2CPP can use an engine kit
and a router script instead: [`router-api.md`](https://steonmod.com/docs/router-api). Coming from a passthrough mod: [`porting.md`](https://steonmod.com/docs/porting).)

- Rust: the `fusion-guest` crate (`crates/fusion-guest`).
- Example: `tools/sdk-world`, a crate yard drawn by a small CPU ray tracer (about 200 lines), with
  its router `routers/sdk-example` and scenario `scenarios/sdk-crate-yard.toml`.
- Reference user: `tools/fake-guest`, Fusion's test game, which shares GPU textures.
- Bevy: the `fusion-guest-bevy` plugin (`sdks/bevy`, its own workspace): add
  `FusionGuestPlugin`, mark a camera, colliders and entities with components ([`sdks/bevy/README.md`](https://steonmod.com/docs/sdk-bevy);
  example `sdks/bevy/examples/world.rs`, router `routers/bevy-example`, scenario `bevy-yard`).
- C and C++: `crates/fusion-guest-c`, a C API over the same Rust SDK (one implementation, so
  both behave the same): link `fusion_guest_c.dll` (import library `fusion_guest_c.dll.lib`) and
  include `include/fusion_guest.h`. Example: `tools/c-world/c_world.c` (a yard drawn by a small
  C ray tracer), router `routers/c-example`, scenario `c-yard`. See "C and C++" below.
- C#: `sdks/csharp/FusionGuest.cs`, one file over the same C API, for BepInEx, MelonLoader and
  OWML plugins or any .NET program (C# 7.3, .NET Framework 3.5 and newer, no `unsafe`). Example:
  `sdks/csharp/example` (`cs-world`, the same yard drawn by C#), router `routers/cs-example`,
  scenario `cs-yard`. See "C#" below.

All of them pass the same checks: `fusion-cli router conform` (picture, lag, collision, entities,
sleep and wake) and Fusion's `guest_world_test` (the player stands on the game's floor, its
entity is listed and moves).

## The loop

```rust
use fusion_guest::{Entity, Event, Guest};

let mut guest = Guest::connect("mygame")?;   // waits until Fusion plays a scenario with your blocks
guest.send_collision_boxes(&[([0.0, -0.25, 0.0], [20.0, 0.25, 20.0])]);   // a 40 m floor
loop {
    for event in guest.update() {             // heartbeat, sleep/wake, frame cap: handled here
        match event {
            Event::Action { request_id, name, .. } => guest.reply(request_id, name == "stop"),
            Event::Spawn { request_id, .. } => guest.reply(request_id, false),
            _ => {}
        }
    }
    if guest.ended().is_some() { break; }      // Fusion closed, or stopped answering for 10 s
    if guest.sleeping() { continue; }          // Fusion put the game to sleep: don't simulate
    let (w, h) = guest.render_size((640, 360));
    guest.picture(w, h, true)?;                // a CPU picture with depth (once per size)
    if let (Some(cam), Some(mut frame)) = (guest.camera(), guest.frame()) {
        // draw from Fusion's camera: cam.camera_to_world, cam.fov_y_degrees
        let (color, depth) = frame.color_and_depth();
        // ... RGBA8 (alpha 0 = nothing), depth in meters in front of the camera ...
        frame.publish();
    }
    guest.send_entities(&[Entity::new(1, "mygame.npc", "Bob", [0.0, 0.0, -3.0])]);
    guest.pace(frame_start, 60.0);             // keeps to Fusion's frame cap
}
```

## What goes where

| Your game has | SDK call | Block port |
|---|---|---|
| ground and walls | `send_collision(&triangles)` or `send_collision_boxes` (cut into 16 m chunks for you; chunks you no longer fill are cleared) | `gives = ["collision"]` |
| NPCs, vehicles, crates | `send_entities(&list)` a few times a second (the whole list each time) | `gives = ["entities"]` |
| a picture | `picture(w, h, depth)` + `frame()` / `publish()`; or `gpu_picture` for shared D3D12 textures | `gives = ["layer:color+depth"]` |
| a character the player plays | `set_pose`, `set_view` (eyes); read `input()` while `possessed()` | `kind = "character"`, `needs = ["input"]` |
| things Fusion asks | `Event::Action` / `Spawn` / `Teleport` → `reply`; `Event::Query` → `answer` | `actions = [...]` |
| events for Lua | `damage`, `died`, `spawned`, `custom(name, data)` | `gives = ["events:damage"]` |
| Fusion's collision around you | `Event::Collision` (a chunk, replacing that chunk) | `needs = ["collision"]` |

Everything is in **Fusion's space**: meters, Y up, right-handed, -Z forward. Convert at the edge
of your game, the way kits do. Say `units = { length = "m", up_axis = "y" }` in your router.

## C and C++

The header's comment has the whole loop. The same calls as the Rust SDK, with a handle:

```c
FusionGuest *g = fusion_guest_connect("mygame");      // or fusion_guest_try_connect each frame
fusion_guest_send_boxes(g, boxes, n);                  // n boxes: center xyz, half size xyz
while (!fusion_guest_ended(g)) {
    uint32_t n = fusion_guest_update(g);               // then fusion_guest_event(g, i, &e)
    FusionCamera cam; uint8_t *color; float *depth;
    if (fusion_guest_camera(g, &cam) && fusion_guest_picture(g, w, h, 1) == 0
        && fusion_guest_frame(g, &color, &depth)) {
        /* draw from cam into color and depth */
        fusion_guest_publish(g, cam.camera_id);
    }
    fusion_guest_send_entities(g, list, count);        // a few times a second
}
fusion_guest_close(g);
```

Event names (`FusionEvent.name`) stay valid until the next `fusion_guest_update`.

The C API also has everything for other blocks: a character (`fusion_guest_input`,
`fusion_guest_set_pose`, `fusion_guest_set_view` with `FUSION_VIEW_IN_WORLD`), collision from
other games around it (`FUSION_EVENT_COLLISION` + `fusion_guest_collision`), events for Lua
(`fusion_guest_damage`, `_died`, `_spawned`, `_custom`), and GPU pictures for Direct3D 11 games
(below). A handle may be used from two threads (a game's main thread and its render thread):
every call holds the handle's lock.

## C#

```csharp
var guest = Fusion.Guest.Connect("mygame");            // or TryConnect each frame from a plugin
guest.SendBoxes(new float[] { 0, -0.25f, 0, 20, 0.25f, 20 });
while (!guest.Ended)
{
    foreach (var e in guest.Update())
        if (e.Kind == Fusion.EventKind.Action) guest.Reply(e.RequestId, false);
    Fusion.Camera cam;
    if (guest.TryGetCamera(out cam) && guest.Picture(640, 360, true))
        guest.PublishFrame(rgba8, depthMeters, cam.CameraId);  // or D3D11Queue (below)
}
guest.Dispose();
```

The same calls as C, in C# shapes: `Update()` returns the events, `SendEntities` takes a list of
`Fusion.Entity`, `TryGetCollision(event.Index, ...)`, `TryGetInput`, `SetPose`, `SetView`.
Windows must find `fusion_guest_c.dll`: put it next to your plugin's DLL or the game's exe.

## Pictures

- **CPU picture** (`picture`): each frame you fill RGBA8 pixels (sRGB, top row first; alpha below
  128 means "nothing here", so Fusion's world shows through) and, with depth, one `f32` per pixel:
  the distance in front of the camera in meters. With depth, Fusion draws it **in 3D**, sorted
  against Godot's props and other games; without, flat on top like an interface. Draw from
  `guest.camera()` so your picture lines up with Fusion's view, and set `frame.camera_id` to the
  camera you drew from (Fusion measures lag with it). Any size works (it's stretched to the
  screen): smaller is cheaper.
- **GPU picture** (`gpu_picture`, `gpu_acquire`, `gpu_publish`): shared D3D12 textures and a
  fence, no copies ([`protocol.md`](https://steonmod.com/docs/protocol), "Picture layers"). Create your device on `gpu_luid()`.
- **GPU picture from Direct3D 11 textures** (C and C#; most injected games): hand Fusion your own
  color and depth textures each frame and the SDK copies them into Fusion's shared textures on
  your GPU (the same copy the Unity and Unreal kits use). From the render thread (a `Present`
  hook): `fusion_guest_d3d11_publish(g, context, color, depth, &mode, flags, camera_id)`. From an
  engine that draws on its own render thread (Unity): `fusion_guest_d3d11_queue` on the main
  thread, then the render thread calls `fusion_guest_d3d11_render_event()` with the id (Unity:
  `CommandBuffer.IssuePluginEvent`). `FusionDepth` says how the depth is stored: the depth
  buffer's own 0..1 values (`kind` 0, with near/far planes and reversed Z), or distances
  (`kind` 1, times `scale` = meters). -1 means the GPU path can't work here (another GPU than
  Fusion's): `fusion_guest_error` says why, and CPU pictures still do. Tested by `sdk-world
  --d3d11` (`conform_d3d11`: 1.7 frames behind Fusion's camera, depth matching its collision).

## Your router

```toml
[router]
id = "mygame"
name = "My Game"
version = "0.1.0"
units = { length = "m", up_axis = "y", health_max = 100 }

[router.link]
protocol = "fusion"

[router.launch]
exe = "{game}/MyGame.exe"

[router.detect]
paths = ["%LOCALAPPDATA%/Programs/MyGame/MyGame.exe"]

[[block]]
id = "mygame.world"
name = "My Game's World"
kind = "world"
gives = ["collision", "layer:color+depth", "entities"]
```

Then `fusion-cli router check routers/mygame`, and play it from a scenario.
