Libraries and modules
Split your mod into files or reuse functions from another mod. The examples target Evospace 0.22.2; the API reference documents version 0.22.2.
require
A path starts with ./, ../ or @. You can load modules from your own mod folder or from a declared dependency.
| Path | Reads |
|---|---|
| require('./ore_washer') | A module beside the requiring file. |
| require('../panels') | A module one folder up. |
| require('@self/Blocks/all') | A module of the mod itself, from its init.lua. |
| require('@VanillaLib') | What that mod's init.lua returned. |
| require('@MyLib/tiers') | A module of that mod. |
- A module is name.luau or name.lua, or a folder with init.luau or init.lua in it. If the path matches more than one module, require reports an error.
- A module returns one value. It runs the first time it is required, and every require after that gets the same value.
- @ModName is the mod's own name or one of its dependencies; names are case-insensitive. Trying to import an undeclared dependency fails and writes an error to the log:
require('@VanillaLib/...') in mod DrillingRigSpeed: that mod is not among its dependencies Using a library
Add the library mod to dependencies so it loads before your mod, then import it with require. The game includes VanillaLib, which provides the helper functions used by Vanilla to set up machines.
{
"name": "DayAndNightSolar",
"version": "1.1",
"title": "Day & Night Solar",
"author": "your name",
"evospace_version": "0.22.2",
"dependencies": ["Vanilla>=0.22.0", "VanillaLib>=0.22.0"],
"description": "Solar panels produce at full output throughout the day and night."
} local Vlib = require('@VanillaLib')
local DayAndNightSolar = {}
local function day_and_night(name)
local base = StaticBlock.find(name).logic_init
return {
logic_init = function(self)
base(self)
EnvironmentCrafter.cast(self).follows_sun = false
end,
}
end
function DayAndNightSolar.post_init()
Vlib.FillBlock("SmallSolarPanel", day_and_night)
Vlib.FillBlock("SolarPanel", day_and_night)
end
db:mod(DayAndNightSolar) Vlib.FillBlock takes a block name without its material prefix and a function. It walks the material tiers, calls the function for every prototype it finds — StoneSolarPanel, CopperSolarPanel and the rest — and writes the fields of the returned table onto the prototype. Here that field is logic_init, wrapped around the one vanilla set.
Modules of your own
A mod reads its own files the same way. Vanilla keeps one file per machine and loads them through a single module:
Vanilla/
info.json
init.lua
Blocks/
all.lua
ore_washer.lua
macerator.lua local vanilla_mod = {}
function vanilla_mod.init()
require('@self/Blocks/all')()
end
db:mod(vanilla_mod) local Vlib = require('@VanillaLib')
return function()
Vlib.FillBlock("OreWasher", require('./ore_washer'))
Vlib.FillBlock("Macerator", require('./macerator'))
end @self in init.lua is the mod folder, so it reaches any file under it. ./ and ../ are read from the file that requires them, which keeps a folder of modules together.
Writing a library
This example exports helper functions without registering new prototypes. Its init.lua creates a table of functions, registers the mod with db:mod and returns the table. Other mods receive that table when they call require('@MyLib').
{
"name": "MyLib",
"version": "1.0",
"title": "My Lib",
"author": "your name",
"evospace_version": "0.22.2",
"dependencies": [],
"description": "Helpers for my mods."
} local MyLib = {}
--- Every recipe of a dictionary takes the same number of ticks.
--- @param dictionary string
--- @param ticks integer
function MyLib.set_ticks(dictionary, ticks)
for _, recipe in pairs(RecipeDictionary.get(dictionary):recipes()) do
recipe.ticks = ticks
end
end
db:mod({})
return MyLib A mod that depends on MyLib reads it by name. The library's init.lua has run by then, and it runs once however many mods require it.
local MyLib = require('@MyLib')
local FastSmelting = {}
function FastSmelting.post_init()
MyLib.set_ticks('SmelterRecipeDictionary', 30)
end
db:mod(FastSmelting) Globals
Each mod has its own global environment. Assigning a global variable affects only that mod. When a name is not defined there, Lua looks it up in the game's global environment. To share values between mods, return them in a library's table.
Beside require, the API globals and the classes of the reference, a mod sees _G, _VERSION, assert, error, ipairs, pairs, next, pcall, xpcall, select, tonumber, tostring, type, typeof, unpack, rawget, rawset, rawequal, rawlen, setmetatable, getmetatable, print, print_info, print_warn, print_err, bit32, buffer, integer, utf8, math, string, table, coroutine.
Editor
The API ships as type definitions in Evospace/lua-api: evospace.d.luau for luau-lsp, which reads Luau itself, and a library folder of LuaCATS stubs for LuaLS. A .luaurc in the folder that holds the mods gives luau-lsp the mod names, so it resolves @VanillaLib the way the game does.