SteonMod
View as Markdown

Unity routers (Fusion's Unity kit)

For Unity games built with IL2CPP (a GameAssembly.dll next to the game's exe). The kit is a MelonLoader mod plus fusion_unity.dll. Unity Mono games (a <Game>_Data/Managed folder, BepInEx 5, OWML) aren't on the kit yet: use your own plugin with the C# SDK (sdk.md).

What the kit does by itself, with no router code:

  • draws the world from Fusion's camera with depth: it finds the camera that really draws the world (games often render a hidden world camera themselves) and moves it to Fusion's camera while it renders, then gives the game its camera back. On Direct3D 11 the picture goes to Fusion on the GPU (about 1 frame behind Fusion's camera); otherwise it's read back;
  • opens bind.map once the game has booted, waits for the world, and puts Fusion's origin on the ground under the game's own view;
  • traces collision around Fusion's camera (if a block gives collision);
  • lists NPCs (if a block gives entities): your bind.entity rules, or anything with a NavMeshAgent; spawns copies of loaded prefabs; teleports the player; remove, set_health, move_to, stop;
  • keeps the game running when it isn't the window in front.

Not yet on the Unity kit: playing as the game's character, player_follows_camera.

Data: [block.bind]

[block.bind]
map = "Level01"                  # the scene to open once the game has booted
camera = "Main Camera"           # the camera whose picture reaches the screen (default: the main 3D camera)
player = "PlayerController"      # a component class on the player (default: the GameObject tagged Player)

[[block.bind.entity]]            # on a block that gives entities; one rule per kind
class = "BaseAi"                 # a component's class name
kind = "animal"
name = "Animal"
health = "m_CurrentHP"           # field paths on that component
max_health = "m_MaxHP"
spawn = "WILDLIFE_Wolf"          # a prefab or object the game has loaded, by name, or a Resources path

Lua: router.lua

The same Router.block and sandbox as on Unreal. Game.* reaches the game's objects through Il2CppInterop reflection:

  • o.field, o.field = v, o:Method(...) on any component (panel:IsEnabled(), ai.m_CurrentHP); o.gameObject.name follows properties; list[1] reads an element (from 1); enums arrive as their names ("Wolf"); positions are { X, Y, Z } in Unity's meters.
  • Unity adds Game.static(class, name) (a static field or property, e.g. a singleton's m_Instance, or a function calling a static method), Game.load_scene(name), Game.component(o, class), Game.position(o), Game.members(o) (what an object has).
  • Game.find(name) finds a GameObject or component by name; Game.class(name) is what to spawn.
  • No player controllers or console: Game.controller() is nil, Game.console() false.

Try every line on the running game before writing it down: fusion-cli game lua "Game.find('Panel_MainMenu'):IsEnabled()" --game-id <id>.

The usual job for Lua on Unity is getting from the boot screen into a world through the game's own menu functions, one step per call of enter (twice a second), and never touching the player's own saves:

Router.block "mygame.world" {
    enter = function()
        local menu = Game.find("Panel_MainMenu")
        if menu and menu:IsEnabled() then
            menu:OnNewGame()             -- the same function the game's button calls
        end
    end,
}

Installing the kit (v0)

The kit's mod is built against the game's own MelonLoader files, so for now it goes into a game with kits\unity\install.ps1 -Game <id> (MelonLoader installed, the game started once with it, the game closed), not one-click setup. Logs: UserData\FusionKit\fusion_kit.log.

Gotchas

  • Don't click into MelonLoader's console window while the game runs: Windows' console selection pauses the game (--melonloader.hideconsole hides it).
  • A copied prefab may need the game's own spawner to come alive: a Lua spawn can call it.
  • Games that render the world several times a frame (an SSAO pass with a wider view) can give depth that doesn't match the picture; conformance says so.
  • Example: example-unity.router.toml (a made-up game in the shape of a real router: a world and its wildlife).