How a mod is built
The files in a mod, mod.lua, splitting code into files and using another mod.
A mod is a folder. The folder name is the mod's id.
Wax\mods\
MyMod\
init.lua the code that runs
mod.lua optional: name, version, dependencies
helpers.lua optional: more code, loaded with requireinit.lua
Wax runs init.lua from top to bottom when the mod loads. It runs it again, from the top, each time the mod
reloads.
print("My mod is loading.")
local window = ui.Window({ title = "My mod" })
window:Label("Hello.")mod.lua
mod.lua describes your mod. It is called the manifest. The file is optional, and so is every field in it.
return {
name = "My mod",
version = "1.2.0",
main = "init.lua",
dependencies = { "Greeter" },
}| Field | What it means | When left out |
|---|---|---|
name | The name shown on the Mods page. | The folder name. |
version | The version shown on the Mods page. | "0.0.0" |
main | The file Wax runs first. | "init.lua" |
dependencies | Ids of mods that must load before this one. | None. |
mod.lua may only hold plain values like the ones above. It cannot call functions.
What every mod can use
These names are ready in every file of your mod. You don't load them.
| Name | What it is |
|---|---|
game | The root of the game: the world, the player and everything else. See The game tree. |
ui | Windows, overlays, notifications, hotkeys and themes. See Windows. |
task | Run code alongside the game. See Tasks. |
Signal | Make your own events. See Signals. |
print, log | Write to the log. See Printing and the log. |
persist, storage | Keep values across reloads and across game sessions. See Saving settings. |
require | Load another file of your mod, or another mod. |
mod | Facts about your own mod. |
raw | Everything UE4SS offers, with no safety checks. For experts. |
Lua's own tools (string, table, math, pairs, ipairs, tostring and the rest) work as usual.
The mod table
print(mod.id) -- the folder name, for example "MyMod"
print(mod.name) -- the name from mod.lua
print(mod.version) -- the version from mod.lua
print(mod.dir) -- the path of the mod's folderEach mod has its own globals
A global variable you set in one mod is not seen by other mods. Two mods can both use the name settings
without clashing.
More than one file
Put shared code in its own file. Load it with require.
local helpers = {}
function helpers.shout(text)
return text:upper() .. "!"
end
return helperslocal helpers = require("helpers")
print(helpers.shout("hello")) -- HELLO!require("helpers")loadshelpers.lua.require("tools.math")loadstools\math.lua. If that file is not there, it loadstools\math\init.lua.- A file runs once. A second
requireof the same name gives back the same value. - What the file returns is what
requiregives you.
Saving any .lua file of the mod reloads the whole mod.
Using another mod
One mod can offer functions to others. What its init.lua returns is what other mods get.
local greeter = {}
function greeter.hello(name)
return "Hello, " .. name .. "!"
end
return greeterA mod that uses it does two things. It lists the id under dependencies:
return {
name = "My mod",
dependencies = { "Greeter" },
}And it loads the other mod with @ in front of the id:
local greeter = require("@Greeter")
ui.Notify(greeter.hello("explorer"))Good to know:
- Dependencies load first.
- When
Greeterreloads, every mod that depends on it reloads too. - If
Greeteris missing or switched off, your mod does not load. The Mods page says which mod it needs. require("@Greeter")without listingGreeterunderdependenciesis an error. The message tells you what to add.
Wax cleans up after you
Everything a mod sets up belongs to that mod: its windows, overlays, hotkeys, tasks and signal connections. When the mod reloads or is switched off, Wax removes all of it. You don't write clean-up code.
Functions that are blocked
A few UE4SS functions are switched off inside Wax mods, because they break the game's Lua state. Calling one raises an error that says why.
| Blocked | Use this instead |
|---|---|
LoopAsync, ExecuteAsync, ExecuteWithDelay | task.spawn, task.delay and task.wait |
RegisterKeyBind, RegisterKeyBindAsync | ui.Hotkey |
RestartMod, RestartCurrentMod, UninstallMod, UninstallCurrentMod, ClearAllDelayedActions | Nothing. Wax reloads mods by itself. |
The originals are still reachable through raw, for example raw.LoopAsync. They can crash the game.
The full list with reasons is in the reference.