Make a mod
A mod is a small folder of Lua files that the Mod Loader runs when the game starts. This page builds the smallest useful one, then adds a window to it.
A mod is a folder in user/mods/<id>/ inside the game folder. It holds a manifest, a file named <id>_mod.lua that describes the mod, and the Lua files the manifest lists. The loader reads the manifest, runs those files, registers the mod's commands and builds its windows.
Rules
- The id is lower-case: it starts with a letter and holds only letters, digits and
_, such ashelloormy_timer. - The folder is named for the id, and the manifest's
idmust match it. - Every file the mod ships is named for its id (
hello_mod.lua,hello.lua,hello_icons.png...), because the client's search path is flat and two mods must not use the same file name. - A mod that fails to load does not stop the others.
/modsin game shows the error.
1. Make the folderuser/mods/hello/Inside the game folder, next to the loader's own files.
2. Write the manifest,hello_mod.luaModLoader.register { id = "hello", name = "Hello", version = "0.1.0", author = "You", description = "Says hello in chat.", tags = { "example" }, loader = 1, tested_with = "Omega / v26.09.416", files = { lua = { "hello.lua" }, }, commands = { { name = "hello", usage = "/hello [name]", help = "Say hello in chat" }, }, windows = {}, }id,nameandversionare required.loader = 1is the loader API the mod was written for; the loader refuses a mod that needs a newer one.files.lualists the Lua files to run, in order. Each entry incommandsmakes the loader register a slash command by itsname;usageandhelpare for the people reading about your mod.windowslists the windows the mod builds, none yet.
3. Write the code,hello.lualocal mod = ModLoader:get("hello") mod:onCommand("hello", function(self, args) local who = string.match(args or "", "^%s*(%S+)") or "Atys" self:say("hello, " .. who .. "!") end)ModLoader:get("hello")gives your mod's object.mod:onCommand(name, function)sets what/hellodoes: the function gets the mod and the text typed after the command.self:say(text)prints[Hello] textin the system info channel.
4. Add it to the listIn
user/mods/modloader_list.lua:ModLoader.installed = { "hello" }
5. Restart the client and type /helloAt login you should see
[Mods] 1 of 1 mods running.Then:/hello [Hello] hello, Atys! /hello Chris [Hello] hello, Chris!
Windows are described in the game's interface XML. mod:addUI(xml) hands the loader a string of it, and the manifest lists the window's id so the loader can check it was created. Change the manifest's windows line:
windows = { "hello_main" },Then replace hello.lua with this:
local mod = ModLoader:get("hello")
mod:addUI([[
<group type="container" id="hello_main" x="200" y="-200" w="240" h="64" posref="TL TL"
title="HELLO" global_color="true" header_color="UI:SAVE:WIN:COLORS:COM"
movable="true" resizer="true" openable="false" opened="true" active="false" savable="true"
pop_min_w="240" pop_max_w="240" pop_min_h="64" pop_max_h="64">
<group id="header_closed" x="0" y="0" w="0" h="16" posref="TL TL" />
<group id="header_opened" x="0" y="0" w="0" h="16" posref="TL TL" />
<group id="content" x="0" y="0" sizeref="hw" w="0" h="0" posref="TL TL">
<view type="text" id="greeting" posref="TL TL" x="8" y="-8" fontsize="12" shadow="true"
global_color="false" color="255 255 255 255" hardtext="" />
</group>
</group>
<tree node="hello_main" />]])
mod:onCommand("hello", function(self, args)
local who = string.match(args or "", "^%s*(%S+)") or "Atys"
self:say("hello, " .. who .. "!")
local win = getUI("ui:interface:hello_main")
win:find("greeting").hardtext = "Hello, " .. who .. "!"
win.active = not win.active
end)The window starts hidden (active="false"). getUI finds it by its full id, win:find("greeting") finds a view inside it, and setting hardtext changes the text. Now /hello also puts the name in the window and shows or hides it. The loader remembers whether a listed window was open when you logged out.
Window XML is easy to get wrong. Copy a working one, such as one of the windows in mods/menu/menu.lua in the mods repository, and change it a little at a time. The loader builds windows once per game session, so a change to the XML needs a client restart; it tells you so.
The full manifest for step 2
ModLoader.register {
id = "hello",
name = "Hello",
version = "0.1.0",
author = "You",
description = "Says hello in chat.",
tags = { "example" },
loader = 1,
tested_with = "Omega / v26.09.416",
files = {
lua = { "hello.lua" },
},
commands = {
{ name = "hello", usage = "/hello [name]", help = "Say hello in chat" },
},
windows = { "hello_main" },
}- Errors.
/modslists every mod with its status and, for one that failed, the error. The client'sclient.loghas XML errors. - Faster testing.
/lrreloads the loader and every mod's Lua without restarting the game. It only works if you addAllowDebugLua = 1;toclient.cfgin the game folder. Windows that already exist are kept, so XML changes still need a restart. - Settings. A mod can keep numbers between logins: list them in the manifest as
settings = { key = default }(whole numbers only), then read and write them withmod:setting("key")andmod:setSetting("key", value). A key you did not declare is an error. All mods share 64 saved slots, so keep the list short. - More. The mods repository has the full reference and the source of every mod on this site, which are good to read and copy from.
