Getting started
Set up your scripts folder, load your first script, and work with modules.
Script location
Place entry scripts in:
text
Documents\Harmony\Red Dead Redemption 2\LUAHarmony uses the Windows Documents known folder, including when Documents has been redirected. Use Settings → LUA → Open Lua Folder to open the correct directory. The folder is named LUA, in uppercase. Startup migrates the old lowercase folder and installs list_gui.lua when that example file does not already exist; existing edits are preserved.
The entry-script list includes regular files directly inside this folder with the lowercase .lua extension. It does not recursively list subfolders or accept symbolic-link entry scripts. An entry file must contain between 1 byte and 1 MiB of source. UTF-8 source is recommended; a UTF-8 BOM is accepted. Entry files are loaded as text, not precompiled Lua bytecode.
Loading and reloading
- Save a script such as
hello_harmony.luain the LUA folder. - Open Settings → LUA and select Refresh Scripts.
- Select the file beneath Lua Files, then select Load.
- Open the page created by the script beneath its load controls.
- After editing the file, select Reload. Use Unload to stop it.
Loading may briefly show Cancel Load. Errors appear in the file's Status description and an error notification. Refreshing the file list does not reload scripts. Scripts do not automatically load at startup.
First script
Save this complete example as hello_harmony.lua:
lua
assert(script.api_version == 2, "This example requires Harmony Lua API v2")
local page = menu.root("Hello Harmony")
page:button("Say hello", function()
util.notify("Hello from " .. script.name)
end, "Shows a message when selected.")
local showOverlay = false
local white = draw.color(255, 255, 255)
page:toggle("Show overlay", false, function(enabled)
showOverlay = enabled
end)
script.on_tick(function()
if showOverlay then
draw.text(32, 80, "My first Harmony script", white, 20)
end
end)menu.root() creates a page in this script's menu. A button runs its callback when selected. A toggle callback receives its new boolean value. Drawing is submitted again on each script update so it remains visible.
Modules and standard libraries
Every loaded entry script has its own Lua VM and globals. Two entry scripts do not share variables or package.loaded. Within one script, require() supports modules using these search patterns:
text
LUA/?.lua
LUA/?/init.luaFor example, create LUA/mylib/palette.lua:
lua
return {
text = draw.color(240, 240, 245),
background = draw.color(20, 24, 32, 210)
}Then use local palette = require("mylib.palette") from the entry script. Modules in subfolders are not separate entries in the script picker.
The runtime opens the Lua base, math, string, table, utf8, and package libraries. It does not open io, os, debug, or the coroutine library. Native DLL module loading is disabled through an empty package.cpath and removal of package.loadlib. Harmony replaces print with its single-string file logger. loadfile and dofile are removed; the replacement module searcher enforces the file boundary independently of package.path.
Scripts are trusted local programs. These library choices do not constitute a security sandbox: the base library remains available, and the exposed native and memory interfaces can affect the local game process. There is no permission manifest, permission prompt, or per-script capability configuration in API v2.