SteonMod
View as Markdown

Unreal routers (Fusion's Unreal kit)

The Unreal kit is a UE4SS mod (a Rust DLL, a little C++, Lua) that Fusion's one-click setup puts in the game. It works on Unreal 4.12 to 5.x, Direct3D 11 and 12. From Unreal's conventions it already does, with no router code:

  • the player is player controller 0's pawn; it opens bind.map from the main menu and waits for the world (and World Partition) to load; picks Fusion's origin on open ground;
  • draws the world from Fusion's camera with depth (scene captures), and hides the game's own view;
  • traces collision around Fusion's camera (if a block gives collision);
  • lists characters near Fusion's camera (if a block gives entities), spawns, removes, sets health, moves and stops NPCs; teleports the player;
  • plays as the game's own character (a character block that needs input): Fusion's keys and mouse go through Unreal's own movement, and Fusion's camera follows its eyes;
  • keeps the game running when it isn't the window in front.

Don't redo any of that in Lua. A router lists only what's particular to the game.

Data: [block.bind] on the world block

[block.bind]
map = "/Game/Maps/Town"          # opened from the main menu; the name alone also matches
player = "BP_Hero_C"             # the game is in its world once the player is one
player_follows_camera = true     # World Partition games load around the player

Data: NPCs, on a block that gives entities

One rule per kind; other characters are listed as "character".

[[block.bind.entity]]
class = "BP_Zombie_C"            # exact class; or is_a = "/Script/Game.BaseEnemy" for subclasses
kind = "zombie"
name = "Zombie"
health = "Health.Health"         # field paths from the actor
max_health = "Health.MaxHealth"
team = 2
spawn = "/Game/Zombies/BP_Zombie"  # what Fusion's Spawn makes for this kind

Find names by asking the running game (Fusion closed): fusion-cli game find zombie --game-id <id> gives classes and objects; fusion-cli game inspect <object> --game-id <id> lists its fields. An Object field's value is another object: inspect it to build a path such as Health.Health. Router Studio does the same with clicks (F4 in Fusion).

Playing as the game's character

[[block]]
id = "mygame.player"
name = "My Game's Hero"
kind = "character"
needs = ["input"]
gives = ["pose"]

No bind data needed: the kit drives player controller 0's pawn.

Lua: router.lua, only when data isn't enough

It runs in a sandbox (Router, Game, Lua 5.4's safe libraries; no files, OS or require):

Router.block "mygame.world" {
    enter = function()                       -- twice a second until in the world
        local menu = Game.widget("WBP_TitleScreen_C")
        if menu then Game.call(menu, "OnStartClicked") end
    end,
}
  • World blocks: enter, ready, anchor, hidden. Entity blocks: describe(object, class, is_character) → { kind, name, health, max_health, team } / false / nil, and spawn = { kind = function(at, rotation) ... end }.
  • Game.*: find, find_all, object, class, class_name, full_name, is_a, get/set (field paths), call, player, controller, world, map, widget, console, trace, spawn, loaded, time, log.
  • A wrong field name fails when the script runs, with the line and what's allowed. router check checks every Game.x/Router.x name before that.
  • fusion-cli router reload routers/<id> [--watch] sends router.toml and router.lua to the running game without restarting it.

Setup (one click for players)

router new --kit unreal writes it; the project folder is the one holding Binaries/Win64:

[router.setup]
installed_when = "{game}/MyGame/Binaries/Win64/ue4ss/Mods/FusionKit/dlls/main.dll"

[[router.setup.require]]
name = "UE4SS"
check = "{game}/MyGame/Binaries/Win64/ue4ss/UE4SS.dll"
guide = "Download UE4SS and unzip it into the game's MyGame/Binaries/Win64 folder."
url = "https://github.com/UE4SS-RE/RE-UE4SS/releases"

[[router.setup.step]]
copy = "{tools}/fusion_unreal.dll"
to = "{game}/MyGame/Binaries/Win64/ue4ss/Mods/FusionKit/dlls/main.dll"

[[router.setup.step]]
copy = "{fusion}/kits/unreal/lua"
to = "{game}/MyGame/Binaries/Win64/ue4ss/Mods/FusionKit/Scripts"

(plus copies of {router}/router.toml and {router}/router.lua into Scripts, and add_line = "FusionKit : 1" to mods.txt; see example-unreal.router.toml).

Gotchas

  • Content in .pak files: read it from the running game. Never unlock encrypted paks.
  • Unreal before 4.13 can't capture depth: the world is drawn behind Fusion's own objects (conformance says so); everything else works.
  • After changing the kit itself, set the router up again (My Games → Set up): an old kit in the game gives confusing failures.
  • Example: example-unreal.router.toml and example-unreal.router.lua (a made-up game in the shape of a real router: about 20 lines of data, Lua only for two menus and one NPC rule).