starlit

Getting started

Where the scripts folder is, how a script is loaded and confirmed, where its controls show up in the menu, and a first script written one step at a time.

Where scripts live

A script is a text file that ends in .lua. All scripts live in one folder:

Documents\Starlit\lua

The client creates the folder at start. If your Documents folder cannot be used, it falls back to %APPDATA%\Starlit\lua. When the client creates the folder it also writes example.lua into it, a small watermark with a checkbox and a colour picker. It never overwrites a file and it does not write example.lua again if you delete it later.

Scripts are read as UTF-8. A byte order mark at the start is skipped. Use any editor you like.

The scripts page

Open the menu, go to the configs tab and press the scripts button at the bottom of the right column. The < back button at the top returns to the configs page.

The left box is called scripts. It has three buttons, then the path of the folder, then one entry for every .lua file.

buttonwhat it does
open folderOpens the folder in the file browser.
refreshReads the folder again. The page also does this by itself whenever you open it.
reload allReloads every script that is running.

Every entry shows the file name, a state, a line of numbers and the buttons that fit the state.

statewhat it meansbuttons
not loadedThe script is not running.load
confirm?We read the file and hashed it. It waits for you.confirm load, cancel
runningThe script is running. The line of numbers shows file size, memory in use and the time of its last call.unload, reload
errorThe script failed to load or we stopped it. The reason is printed under the entry.load

The right half of the page has one group box for every running script. The box is titled with the file name and holds the controls the script made. Under the boxes is script log, which shows the last 14 lines that scripts wrote with log.info.

A script can also put its groups on the rage, legit, visuals and misc pages of the menu, and open a tab of its own after configs. Two switches on the scripts page belong to you: show script groups in pages (on by default) for all scripts, and show in pages for one script. When either is off, the groups stay in the box of the script on the scripts page, so every control stays within reach. The page also has reset overrides, which gives back every setting and rage value that scripts hold, and reset menu style, which puts your own look of the menu back until you press allow script styles.

Load a script and confirm it

  1. Press load. We read the file once, compute its SHA-256 hash and keep the bytes in memory.
  2. If you have not confirmed these exact bytes before in this session, the entry changes to confirm?. The line under the name shows the first 16 characters of the hash. The confirm load button is red.
  3. Press confirm load within 10 seconds. We start the script from the bytes we hashed. We do not read the file a second time, so a change made between the two clicks cannot get in.
  4. If you wait longer than 10 seconds, the click does nothing and the entry goes back to not loaded. Press load again.
  5. cancel throws the waiting bytes away.

We ask for this confirmation because a script is code from somebody else. The hash is the proof that the code you looked at is the code that runs.

How edits are confirmed again

The confirmation belongs to the bytes, not to the file name. Change one character, save, and the file has a new hash. The next load or reload shows confirm? again.

An unchanged file does not ask. Once you have confirmed a hash, load and reload start that file at once for the rest of the session. We keep 64 confirmed hashes in memory and drop the oldest when the list is full. Nothing is written to disk, so after you restart the game every script asks once more.

A running script keeps running the version it started with. Saving the file changes nothing until you press reload.

Reload and unload

unload runs the script's events.on_unload handler, writes its saved control values, and frees its memory. The entry goes back to not loaded.

reload does an unload and then a load of the file as it is now. If you edited the file, the entry shows confirm? and the script does not run until you confirm. reload all does this for every running script.

If you delete the file of a running script, the entry stays in the list so that you can still unload it. After the unload it shows the note file removed until the next refresh.

The script log

log.info(value) writes one line to the script log box. The line is shown as [file name] text, where the file name has no .lua. The box shows the last 14 lines and we keep the last 64. See log.info for the limits.

Errors that stop a script are not written to this box. They are printed under the entry of the script on the left.

A first script

This walk-through makes a script called hello.lua that draws a line of text and lets you change its font from the scripts page.

Step 1: create the file

Open the folder with open folder. Make a new text file and name it hello.lua. The name must end in .lua and be 5 to 64 characters long, extension included. The other file name rules are in Limits, files and saved values.

Step 2: write the script

local show = ui.checkbox("show text", true)
local font = ui.combo("font", { "small", "ui", "header" }, 2)

events.on_paint(function()
  if not show:get() then return end
  local _, face = font:get()
  draw.text(30, 200, "hello from hello.lua", 255, 255, 255, 255, face)
end)

log.info("hello loaded")

Line by line:

  • Lines 1 and 2 run once, when the script loads. ui.checkbox and ui.combo add controls to the box of this script on the scripts page and return handles. The combo has three items, and the third argument, 2, selects the second one.
  • events.on_paint(fn) registers a function that runs once for every frame. Drawing is only allowed inside that function.
  • show:get() returns true or false.
  • font:get() returns two values, the 1-based index of the selected item and the item's text. The script keeps the text, which is also a valid font name for draw.text.
  • draw.text(x, y, text, r, g, b, a, font) draws at pixel position 30, 200 measured from the top left of the screen. The four colour numbers are 0 to 255.
  • log.info writes the last line to the script log.

Step 3: load it

Press refresh, then load next to hello. The entry changes to confirm?. Press confirm load. The state changes to running.

Step 4: look at the result

In the right half of the page a box titled hello appears with a checkbox and a combo. The line [hello] hello loaded is in the script log. Close the menu to see the text on the screen. Scripts draw under the menu, so while the menu is open, the menu covers the text if they overlap.

Change the font in the combo and the text changes with it. Untick the checkbox and the text goes away. The client remembers both values for the next time you load hello.lua. See saved values.

Step 5: change the script and reload

Change the text in draw.text, save the file and press reload next to hello. The entry shows confirm? because the hash changed. Confirm it and the new text appears.

Step 6: see an error

Change draw.text to draw.txt, save, reload and confirm. The script loads, because the mistake is inside a function that has not run yet. On the first frame the call fails with attempt to call a nil value (field 'txt'). That is the first strike. We count it and do not show it. On the second frame the same error is the second strike, and we stop the script. The entry shows error and the message:

hello:7: attempt to call a nil value (field 'txt')

The number after the script name is the line in your file. A syntax error is reported the same way, when you press confirm load, and the script does not start. How strikes work is described in How scripts run.

While you develop, catch errors yourself so that you see the first one:

events.on_paint(function()
  local ok, err = pcall(function()
    draw.text(30, 200, "hello", 255, 255, 255)
  end)
  if not ok then log.info(err) end
end)

pcall returns false and the message when the inner function fails. The message goes to the script log, at most 20 lines per second.

Reading order

How scripts run explains frames, budgets and strikes. What scripts can do maps the capabilities: reading the match, improving the ragebot, tuning movement and the world, restyling and building visuals, and extending the menu. The reference pages follow, one for each module. The examples page has complete scripts that combine them.

next: How scripts run