AoE4 mod development: Reloading a SCAR script in-game without restarting

Hi everyone,

I’m developing an AoE4 custom scenario and have been using loadfile() to reload its SCAR script from a local project folder while the game is running.

This has helped me iterate on script logic and a custom XAML panel without restarting the game for every change. Here is the workflow that worked in my setup.

1. Launch AoE4 with -dev and start your custom scenario

The scenario still needs to be built and loaded normally to begin with.

Keep an editable copy of its .scar source in a local folder. For example:

D:/aoe4_dev/my_scenario.scar

The path must point to an existing file on your computer.

2. Add a development loader to that source file

ReloadPrototypeScar() is a custom function, not a built-in AoE4 command. This is a simplified version:

function ReloadPrototypeScar()
    local sourcePath = "D:/aoe4_dev/my_scenario.scar"

    local loadOk, chunk, loadError = pcall(loadfile, sourcePath)

    if not loadOk then
        print("[Dev Reload] loadfile failed: " .. tostring(chunk))
        return false
    end

    if chunk == nil then
        print("[Dev Reload] Could not load source: " .. tostring(loadError))
        return false
    end

    local runOk, runError = pcall(chunk)

    if not runOk then
        print("[Dev Reload] Script execution failed: " .. tostring(runError))
        return false
    end

    print("[Dev Reload] SCAR source reloaded.")
    return true
end

Replace the example path with your own absolute path. I use forward slashes.

3. Make sure you are using the SCAR console

This caused some confusion during my testing.

In my setup, Ctrl+Shift+~ opens/closes the console, and Alt+Shift+~ changes the console context. Check the prompt and switch until it says SCAR.

My scenario functions were not available in the Global context. Entering the command there did not reload my scenario.

You can check whether the helper is available in the current context:

print(type(ReloadPrototypeScar))

It should print function. If it prints nil, the helper is not defined in that context yet.

4. Bootstrap the script from disk

The sequence that first worked for me was:

assert(loadfile("D:/aoe4_dev/my_scenario.scar"))()

Then:

ReloadPrototypeScar()

The first command loads and executes the external source file, which also defines the loader function in the SCAR context. The second command calls that loader.

This does not mean both commands are always required. The direct loadfile() command is useful when the running scenario does not have the helper yet, or has an older version with an incorrect source path.

Once the correct helper is available, my usual edit loop is:

  1. Edit and save the external .scar file.
  2. Run ReloadPrototypeScar() in the SCAR console.
  3. Check the new console output and try the changed behavior.

For these script edits, I do not rebuild the packaged mod each time.

Refreshing UI and preserving state

Reloading SCAR does not automatically refresh an existing XAML panel. In my full implementation, I remove the old panel and recreate it after a short delay. The simplified loader above only reloads the script; it does not include that UI refresh logic.

Also, executing a whole source file again runs its top-level code again. Initialization, rules and callbacks need to be managed so they do not reset the current session or register twice.

This workflow reloads SCAR code. Changes to the map, blueprints or other packaged assets may still require rebuilding and starting a new session.

The absolute-path loader is for local development and should be removed or disabled before publishing the mod.

Has anyone used a similar workflow? I would be interested in hearing how other modders handle SCAR reloads, runtime state and XAML panel refreshes.