Script Extender Lua Setup - LaughingLeader/BG3ModdingTools GitHub Wiki
Table of Contents
Prerequisites
This tutorial assumes you already have a mod project created / working in-game.
I recommend setting up the mod as loose folders in the data folder (Data\Mods\ModFolder
, Data\Public\ModFolder
etc), so when you change scripts and reload them via the reset
command, you can see these changes immediately, rather than having to create another pak and restart the game.
Script Extender Setup
Lua scripting requires the Script Extender, a project that makes Osiris/lua scripting possible in Baldur's Gate 3. Out of the box it also enables achievements, and provides a console window for running scripting functions, among many other features.
Updater
Grab the latest Script Extender Updater if you haven't already. Put DWrite.dll
in your Baldur's Gate 3\bin
folder. Launch the game once so the updater will download the latest extender release.
Config.json
This file tells the script extender which features to enable for your mod project. You can either create the file manually, or grab the sample.
The file should be located here:
Mods\ModFolder\ScriptExtender\Config.json
What we want to enable lua scripting are the following settings:
{
"RequiredVersion": 20,
"ModTable": "REPLACE_ME_WITH_YOUR_MOD_ID",
"FeatureFlags": ["Lua"]
}
Replace the ModTable
value with a unique name for your mod. This will be the global table key the extender stores all of your mod's global variables (your mod's 'environment'), so it's very important to set this to a unique name.
With this file set, the extender will enable lua scripting for your mod, and look for the related bootstrap scripts to load.
Property | Description |
---|---|
RequiredVersion | This is the required extender version, and is used to enable some additional features, so generally this is always the latest. |
ModTable | The table key name used for your mod's scripting environment. Must be unique, and can be accessed in scripting within the global Mods table (Mods.LeaderLib for instance). |
FeatureFlags | An array of specific flags to enable for the mod. |
Feature Flags
ID | Description |
---|---|
Lua | Simply enables Lua scripting. Must have a ModTable set. |
ScriptExtenderSettings.json
ScriptExtenderSettings.json
is a config file from the user side of the script extender. We want to set this up to enable the console window and developer mode.
Create a new text file and rename it to ScriptExtenderSettings.json
in the game's bin folder (the same folder you placed DWrite.dll
).
Place the following text inside:
{
"CreateConsole": true,
"DeveloperMode": true,
"EnableLuaDebugger": true,
"EnableLogging": true,
"LogCompile": true,
"LogFailedCompile": true,
"LogRuntime": true
}
These are some basic settings that will allow better development with the extender. Logging is enabled so we have files we can check when debugging issues or looking at the various rules that can happen.
Property | Description |
---|---|
CreateConsole | Enables the script extender console window, which will output console text and allow running lua code directly in the console. |
DeveloperMode | Can be checked in scripts to enable developer features (Ext.IsDeveloperMode()). |
EnableLuaDebugger | Allows hooking the lua debugger to the game with vscode. |
EnableLogging | Enables logging the various aspects of the game. Keep in mind logs can get fairly big, so keep an eye on your logs folder. Default directory is %LOCALAPPDATA%\Larian Studios\Baldur's Gate 3\Extender Logs |
LogCompile | Enables logging for the Osiris compiler when it's merging story scripts. Useful to see issues that cause working story errors. |
LogFailedCompile | Enables logging for the Osiris compiler when compiling fails (may be redundant if LogCompile is enabled). |
LogRuntime | Enables logging for the console window / lua scripting. |
LogDirectory | For changing the directory logs are stored in. Make sure to escape \ in json (\\ ). |
Visual Studio Code Setup
Visual Studio Code is the preferred editor for Divinity scripting. Install the following:
Workspace Setup
Create a new text file somewhere appropriate (wherever you want to store your workspace files outside of your mod folders, such as C:\Modding\BG3\Workspaces
), then rename it to MyModName.code-workspace
, replacing MyModName with whatever your mod's name is.
Edit it with your preferred text editor (Notepad, Notepad++, etc. VSCode may try and open it as a workspace) and enter the following:
{
"folders": [
{
"path": "C:\\Games\\Steam\\steamapps\\common\\Baldur's Gate 3\\Data\\Mods\\ModFolder",
"name": "Mods"
},
{
"path": "C:\\Games\\Steam\\steamapps\\common\\Baldur's Gate 3\\Data\\Public\\ModFolder",
"name": "Public"
},
{
"path": "C:\\Modding\\BG3\\ReferenceScripts",
"name": "ReferenceScripts"
}
],
"settings": {
"search.exclude": {
"**/*.raw": true,
"**/Story/*.div": true,
"**/*.osi": true,
"**/*.dat": true
},
"Lua.workspace.library": [
"C:\\Modding\\BG3\\ReferenceScripts"
],
"Lua.diagnostics.libraryFiles": "Disable",
"Lua.diagnostics.workspaceRate": 25,
"Lua.diagnostics.workspaceDelay": 5000,
"Lua.workspace.preloadFileSize": 10000,
"Lua.diagnostics.globals": [
"Osi",
"Ext"
]
}
}
This is a basic workspace file that includes two folders - Your mod's Mods\ and Public\ folders. Be sure to replace the path values with the actual path to your mod's folders, replacing \
with \\
.
The last folder, "ReferenceScripts", should be a folder you create external to your actual mod. We'll use that folder to store lua reference scripts, such as Osi.lua. Including this folder in the workspace is mainly to make checking references easier, as this isn't required to make auto-completion work.
The ReferenceScripts folder must be included in the Lua.workspace.library
setting, so the global variables in the Osi automatically show up in every lua environment. Without this setting, you will get warnings of an "undefined global" when trying to use the Ext table, and you won't have auto-completion.
Lua.workspace.library
Each individual folder in a workspace is treated as its own isolated lua environment. The Lua.workspace.library
setting is what allows Lua globals, from the included folders, to show up these isolated environments. Therefore, if you want a mod or the extender's globals to show up when working on a mod, folders need to be added to Lua.workspace.library
, as adding a folder to the workspace itself won't work for this purpose.
Other Settings
The search.exclude
setting lets us ignore the various osi compilation files from searching, so if you look for an Osiris function, it should either show your code or story_header.div.
The Lua.diagnostics.workspaceRate
and Lua.diagnostics.workspaceDelay
settings reduce the extension's CPU usage, for better overall performance, though this can be tweaked or removed as desired.
Lua.workspace.preloadFileSize
is needed if your references folder includes the various definitions for Osiris functions as well (all usable in lua).
Lua.diagnostics.globals
is an array of variables to consider as global, even without definition from a script. Including "Osi" here allows you to use it without being warned of an "Undefined global", even if you choose to not include the annotations for the Osi table.
Scripting Setup
Finally with our workspace and the script extender all set up, we can begin to set up actual lua scripting.
References
First grab copies of the various extender scripts here, and place them in a folder external to your mod (such as C\\Modding\BG3\\ExtenderScripts
). We don't want to include these in our mod, just use them in vscode for referencing and auto-completion.
- Osi.lua
This is a generated lua file that provides annotations for all the Osiris functions (Osi.ApplyStatus
etc). - ExtIdeHelpers.lua
This is a generated lua file for the Script Extender itself, that contains the various functions in
Ext
, and all the related types.
Bootstrap Scripts
To finally begin lua scripting, we need to create the two script files the extender looks for. These are our entry points into lua scripting, and from there we can load other scripts.
Create the Lua script folder:
Mods\ModFolder\ScriptExtender\Lua\
Then create two text files, and rename them to BootstrapServer.lua and BootstrapClient.lua:
Mods\ModFolder\ScriptExtender\Lua\BootstrapServer.lua
Mods\ModFolder\ScriptExtender\Lua\BootstrapClient.lua
The extender will attempt to load BootstrapServer.lua
on the server side, and BootstrapClient.lua
on the client side. In a multiplayer context, the host is both the server and a client, and all players connecting are clients. In singleplayer, both the server and client sides co-exist.
Required Scripts
Name | State |
---|---|
BootstrapServer.lua |
Server Side |
BootstrapClient.lua |
Client Side |
From here, these scripts can load other scripts with Ext.Require
. The path to scripts are relative to the Lua folder, so if you had a file setup like this:
Mods\ModFolder\ScriptExtender\Lua\BootstrapClient.lua
Mods\ModFolder\ScriptExtender\Lua\BootstrapServer.lua
Mods\ModFolder\ScriptExtender\Lua\Server\SkillMechanics.lua
BootstrapServer would load SkillMechanics.lua
with Ext.Require("Server/SkillMechanics.lua")
. Script loading only needs to happen once.
Tips
Resetting Scripts
- Go to the extender console window, and hit enter to enable text input.
- Next, type
silence off
and hit enter to send the command. This will allow print messages to appear in the window while in input mode. - The extender command,
reset
, and then hit the enter key.
All lua scripts will be reloaded again.
Note that your mod will need to be loosely in the Data folder for script changes to be reloaded, since pak data itself isn't reloaded.