# The Router API (v0)

A **router** splits one game into blocks (see [`block-manifest.md`](https://steonmod.com/docs/block-manifest)).
The Router API is how a router tells the game's **kit** where those blocks are inside the
game: which character is the player, which objects are its NPCs, how to get from the main
menu into the world.

It's made to need as little as possible:

1. **The kit guesses first.** Every kit knows its engine's conventions (on Unreal: player
   controller 0's pawn is the player, characters are `Character` actors). Most of a
   router is already done before you write anything.
2. **Data next.** What the kit can't guess goes in `router.toml`, under each block's
   `[block.bind]`. Fusion checks it like the rest of the manifest.
3. **Lua last.** Only what data can't say (clicking through a character creation screen)
   goes in `router.lua`, next to `router.toml`.

The same names work on every kit. The Unreal kit (`kits/unreal`) and the Unity kit
(`kits/unity`, step 7.1) both read the data and run `router.lua` (see "On the Unity kit"
below for what differs). The example is VEIN's router:
`routers/vein/router.toml` and
`routers/vein/router.lua`.

## What the kit does by itself

| | Unreal kit default | Change it with |
|---|---|---|
| The player | player controller 0's pawn, once it's a `Character` | `bind.player` (its class) |
| Getting into the world | waits for the player; opens `bind.map` if the game is elsewhere (its main menu) | `bind.map`, Lua `enter` |
| World loaded | the player exists for 3 s, World Partition finished loading, ground under it (60 s at most) | Lua `ready` |
| Fusion's origin | open ground near the player, facing something to see | Lua `anchor` |
| Hidden from Fusion's view | the player (Fusion's camera replaces it; it can't be hurt either) | Lua `hidden` |
| The game's own view | turned off once Fusion shows the world | `bind.hide_game_view = false` |
| Loading around Fusion's camera | off | `bind.player_follows_camera = true` |
| Collision | traced around Fusion's camera, if the world block gives `collision` | |
| Entities | `bind.entity` rules, then any other character, if a block gives `entities` | `bind.entity`, Lua `describe` |
| Spawning | the `spawn` asset of the entity kind Fusion asks for, feet on the ground | `bind.entity.spawn`, Lua `spawn` |
| Window focus | the game keeps running when its window isn't in front | |
| Actions | `remove`, `set_health` (the rule's `health` field), `move_to` (Unreal's AI move helper) and `stop` on the entities it listed, and teleports of the player; a block's `actions` in router.toml say which Fusion offers | Lua `actions` |
| Playing as the character | a `character` block that needs `input`: Fusion's keys and mouse drive the player through Unreal's own `SetControlRotation`, `AddMovementInput` and `Jump`; its eyes (the game's camera) and feet go back to Fusion, whose camera follows them. Its pose goes to Fusion every frame | |

## Data: `[block.bind]`

```toml
[[block]]
id = "vein.world"
kind = "world"
gives = ["layer:color+depth", "collision"]
...
[block.bind]
map = "/Game/Vein/Maps/ChamplainValleyDemo"   # opened from the main menu
player = "BP_VeinPlayerCharacter_C"           # in the world once the player is one
player_follows_camera = true                  # the game loads its world around the player

[[block]]
id = "vein.npcs"
kind = "npc_group"
gives = ["entities"]
...
[[block.bind.entity]]
class = "BP_Zombie_C"                 # exactly this class
kind = "zombie"
name = "Zombie"
health = "Health.Health"              # field paths
max_health = "Health.MaxHealth"
team = 2
spawn = "/Game/Vein/Zombies/BP_Zombie"  # what to spawn when Fusion asks for a "zombie"

[[block.bind.entity]]
is_a = "/Script/Vein.BaseVehicle"     # this class or any class made from it
kind = "vehicle"                      # name defaults to the class, made readable
```

| Field | Blocks | Meaning |
|---|---|---|
| `map` | world | the map (level, scene) to open when the game isn't in it |
| `player` | world | the class of the game's player character |
| `player_follows_camera` | world | keep the game's own character under Fusion's camera (default false) |
| `hide_game_view` | world | turn the game's own view off once Fusion shows it (default true) |
| `camera` | world | the game camera whose picture Fusion shows, by its object's name (default: the kit picks the game's main 3D camera) |
| `anchor` | world | Fusion's origin in the game's units and axes, `[x, y, z, yaw]` (yaw in degrees, where Fusion's forward faces); default: the kit picks one (Lua `anchor` on the Unreal kit) |
| `entity` | blocks that give `entities` | a list of rules, checked in order |

Each `entity` rule has `class` **or** `is_a`, a `kind`, and optionally `name`, `health`,
`max_health` (field paths like `Health.Health`), `team` and `spawn` (an asset or class
path). Two rules can't spawn the same kind.

## Lua: `router.lua`

The script runs once when the game starts. It gives blocks functions:

```lua
Router.block "vein.world" {
    enter = function()
        if Game.class_name(Game.player()) ~= "BP_CharacterCreationPawn_C" then
            return
        end
        local create = Game.widget("WBP_CharacterCreation_C")
        if create then
            create:OnRandomNameSet("Fusion Scout")
            ...
        end
    end,
}
```

| Function | Blocks | Called | Returns |
|---|---|---|---|
| `enter()` | world | twice a second until the game is in its world, after the kit opened `bind.map` | nothing |
| `ready(player)` | world | when the kit thinks the world is ready | `false` to keep waiting |
| `anchor(info)` | world | once, in the world | `{ x, y, z, yaw }`: Fusion's origin |
| `hidden(info)` | world | when the captures are made | a list of objects Fusion's view doesn't draw |
| `describe(object, class, is_character)` | gives `entities` | 4 times a second per pawn near Fusion's camera | `{ kind, name, health, max_health, team }`, `false` to leave it out, `nil` for the data rules |
| `spawn = { <kind> = function(at, rotation, info) }` | gives `entities` | when Fusion asks for that kind | the new object, or nil |
| `actions = { <name> = function(entity, args) }` | gives `entities` | when Fusion runs that action on one of the block's entities, before the kit's own | true if done, false if not |

`info` is `{ world, controller, pawn, anchor }`. `Router.settings` holds the scenario's choices
for the router's block settings (`[[block.setting]]`, see [`block-manifest.md`](https://steonmod.com/docs/block-manifest)), by setting id,
as text (`tonumber(Router.settings.walkers)`); Fusion sends them when it connects. Positions are in the game's own units
and axes (`router.toml`'s `units`), as `{ X, Y, Z }`; rotations as `{ Pitch, Yaw, Roll }`.

A mistake stops the script with its line and what's allowed:

```
router.lua:15: Router.block "vein.world": unknown field `entr` (a world block can have: anchor, enter, hidden, ready)
```

The kit then logs it in `fusion_kit.log` (next to the kit's `main.dll`) and does nothing
else, so a broken router can't half-run.

### `Game`: the game's objects

| Function | |
|---|---|
| `Game.find(class)` / `Game.find_all(class)` | the first / every live object of a class (short name, subclasses too) |
| `Game.object(path)` | an object by full path, e.g. `/Script/Engine.Character` |
| `Game.class(path)` | a class by path, loading its asset if needed (`/Game/Zombies/BP_Zombie`) |
| `Game.class_name(o)`, `Game.full_name(o)`, `Game.valid(o)`, `Game.is_a(o, class)` | about an object |
| `Game.get(o, "A.B")` / `Game.set(o, "A.B", v)` | a field by path; `nil` / `false` instead of an error |
| `Game.call(o, "Name", ...)` | a method by name (the same as `o:Name(...)`) |
| `Game.controller()`, `Game.player()`, `Game.world()`, `Game.map()` | the player controller, its pawn, the world, the map's path |
| `Game.console(command)` | runs a console command |
| `Game.trace(from, to, ignore)` | what a line hits (sight), or nil |
| `Game.spawn(class, at, rotation)` | a new object, or nil |
| `Game.widget(class)` | the live widget of that class on screen, or nil |
| `Game.loaded()` | true once the world around the player finished loading |
| `Game.time()`, `Game.log(fmt, ...)` | seconds; a line in `fusion_kit.log` |

Objects also have the game's own fields and methods: `zombie.Health.MaxHealth`,
`widget:OnRandomNameSet("Fusion Scout")`. Use `Game.get` when a field might be missing.

### The sandbox

The script sees `Router`, `Game`, and Lua's safe libraries (`math`, `string`, `table`,
`utf8`, `os.clock/time/date`, `pairs`, `pcall`, ...). It can't open files, run programs,
load other code (`require`, `load`) or use the kit's own functions. It can do anything
the game itself can do through its objects: that's what reflection is (trust tiers, step
6.2, decide who may publish what).

### Language and types

Router scripts are **Lua 5.4** (on Unreal, UE4SS's own Lua), not Luau like addons:
[`docs/decisions/0004-router-api.md`](https://steonmod.com/docs/adr-0004) says why. Keep to the common subset when you can.
[`docs/types/router-api.lua`](https://steonmod.com/docs/files/router-api.lua) has the types for
lua-language-server, so an editor or `lua-language-server --check` catches a wrong
function name or argument before the game starts.

## Installing a router into a game

Fusion's one-click setup copies the kit and the router (its `router.toml` and
`router.lua`) into the game (`[router.setup]`, see [`block-manifest.md`](https://steonmod.com/docs/block-manifest)). For development:
`kits\unreal\install.ps1 -Game vein [-Dev]`.

## Router Studio

Router Studio (F4 in a session, or **Create → Router Studio**) writes the same
`[block.bind]` data by pointing at the running game's objects: "This is the player", "This
map", "These are NPCs" (with health fields found by clicking through an object's fields).
"Test live" has the kit try the router on the running game, "Apply live" makes the game use it
now (the kit's `reload`). `fusion-cli game` asks the same questions from the command line, e.g.
`fusion-cli game find zombie --game-id vein`.

## Not in v0 yet

- Playing as the character on the Unity kit (no common way to move a Unity game's player).
- Hooks on the game's own functions ("when a zombie dies").
- Reloading `router.lua` by itself without restarting the game (step 5.7); a new `router.toml`
  reloads live (Router Studio's "Apply live"), and runs `router.lua` again with it.
- `router check` running the types and these checks before the game starts (step 5.7).

## On the Unity kit (step 7.1, v0)

The Unity kit (`kits/unity`, a MelonLoader mod; [`kits/unity/README.md`](https://steonmod.com/docs/kit-unity)) reads the same
`[block.bind]` data. What it does by itself:

| | Unity kit default | Change it with |
|---|---|---|
| Getting into the world | opens `bind.map` (a scene name) once Fusion asks, if the game is elsewhere | `bind.map` |
| The picture | the game's main 3D camera's final picture (with the game's own post effects), drawn from Fusion's camera | `bind.camera` |
| Camera rigs | the camera seen rendering the world with the most layers is moved to Fusion's camera (games that render a hidden world camera themselves) | |
| Fusion's origin | the ground under the game's own view, facing where it looks | `bind.anchor` |
| Collision | vertical physics traces around Fusion's camera, if the world block gives `collision` | |
| Entities | `bind.entity` rules (`class` = a component's class name, `health` = a field path on it); with none, anything with a `NavMeshAgent` | `bind.entity` |
| Window focus | the game keeps running when its window isn't in front (even if it tries to turn that off) | |

| The player | `bind.player` (a component class), or the GameObject tagged `Player` | `bind.player` |
| In the world | the scene is `bind.map` (if set) and no scene loaded for a second | Lua `enter`, `ready` |
| Spawning | a copy of the `spawn` prefab of the entity kind Fusion asks for (a prefab or object the game has loaded, by name, or a `Resources` path), on the ground there | `bind.entity.spawn`, Lua `spawn` |
| Teleport | moves the player (its `CharacterController` off while it moves) | |
| Actions | `remove`, `set_health` (the rule's `health` field), `move_to` and `stop` (the entity's `NavMeshAgent`) | Lua `actions` |

**`router.lua` on Unity** runs in the kit's own Lua 5.4 (inside `fusion_unity.dll`), with the
same `Router.block` checks and sandbox. `Game.*` reaches the game's objects through
Il2CppInterop's reflection: `o.field`, `o.field = v` and `o:Method(...)` work on any component
(`panel:IsEnabled()`, `ai.m_CurrentHP`), `o.gameObject.name` follows properties, `list[1]` reads
an element (counting from 1). Enums arrive as their names (`"Wolf"`). Positions are
`{ X, Y, Z }` (also `x, y, z`), Unity's own meters. Unity has no player controllers or
console (`Game.controller()` is nil, `Game.console()` false), and adds:

| Function | |
|---|---|
| `Game.static(class, name)` | a static field or property (singletons: `Game.static("GameManager", "m_Instance")`), or a function calling a static method |
| `Game.load_scene(name)` | opens a scene |
| `Game.component(o, class)` | a component on the same GameObject (or its children) |
| `Game.position(o)` | where an object is |
| `Game.members(o)` | what any object has (fields and methods), for exploring a game with `fusion-cli game lua` |

`Game.class(name)` is what to spawn: a loaded prefab or object by name, or a `Resources` path.

Try any of it on the running game: `fusion-cli game lua "Game.find('Panel_MainMenu'):IsEnabled()" --game-id thelongdark`
runs the snippet in the router's sandbox and prints what it returns.

Not yet on the Unity kit: `player_follows_camera`, possessing a Character block.

