Running and reloading
Lesson 9.3
Time to see your mod in the game. This lesson shows how to start the game with your mod, how to change code while the game runs, and where your messages end up.
clight run
Inside your mod’s folder, run:
> clight run
packed C:\Projects\my_mod\build\my_mod-1.0.0.clmod (3 files, 0 KiB)
running C:\Games\Celestial\Celestial.exe with my_mod (C:\Projects\my_mod, hot reload on)
It does two things:
- It checks and packs the mod, like
clight build. - It starts Celestial in development mode with your project folder loaded next to your normal mods.
The game reads your mod straight from the project folder, so what you save is what runs.
clight finds the game because the SDK sits in the game’s folder. If you moved the SDK, tell it where the game is with clight run --game C:\Games\Celestial.
Create or open a singleplayer world. When you join, the template mod greets you in chat.
Your installed mods in the mods folder are never touched. When you close the game, nothing of your test mod stays behind except the files it saved, in build/run/my_mod/data/.
Hot reload
In development mode the game checks your scripts twice a second. When a .lua file changes, it runs that file again, plus every file that requires it. The log says:
[my_mod] hot reloaded scripts/main.lua
The handlers from the old version are removed and the new ones take their place. Setup events that already happened, like command registration, run again for the new code, so a changed command works straight away.
Try it now: change the welcome text in scripts/main.lua, save, and join the world again. The new text shows up without restarting the game.
Other mods can notice a reload too: lifecycle.mod_reload runs after every hot reload that worked.
Some things can’t change while the game runs:
- New items and blocks. They are registered once at startup. A reload that adds one logs
restart requiredand ignores it until you restart. - The manifest. Restart after changing
clight.mod.json. - Textures, models and language files are resources, not scripts. Press F3 + T to reload resources, like with resource packs.
If the new code has an error, the reload fails and the old code keeps running. Fix the error, save again, and it reloads.
Keeping values across reloads
A reload runs your file from the top again, so its top-level local variables start fresh:
local joins = 0 -- back to 0 after every reload
For values that should survive a reload, use mod.persist. It is a table that lives as long as the game runs:
mod.persist.joins = mod.persist.joins or 0
cl.on("player.join", function(event)
mod.persist.joins = mod.persist.joins + 1
cl.log("joins so far:", mod.persist.joins)
end)
The or 0 part only sets the value the first time. mod.persist is not saved to disk: it is empty again after a restart. Config shows how to keep values in a file.
The log
Everything your mod writes goes into the game’s log file: logs/latest.log in the Celestial folder. Each line has the time, the level, and your mod’s id in brackets:
[18:04:12.511] [20064/info] [my_mod] my_mod 1.0.0 loaded
Open the file in your editor, or keep it open in a terminal while you play:
> powershell Get-Content C:\Games\Celestial\logs\latest.log -Wait -Tail 20
cl.log
cl.log writes one line at the info level. Like print, it joins its values with spaces:
cl.log("player", "Alex", "has", 3, "rubies")
-- [my_mod] player Alex has 3 rubies
If the first value contains a % and there are more values, it works like string.format:
cl.log("%s has %d rubies", "Alex", 3)
-- [my_mod] Alex has 3 rubies
There is one function per level, from chatty to serious:
cl.log.debug("checking slot %d", 4)
cl.log.info("loaded")
cl.log.warn("config value %s is out of range", "speed")
cl.log.error("could not find the ruby block")
Use warn for things that are odd but not broken, and error for things that are really wrong. debug and trace are for details you only want while developing; the game may hide them.
print works too. It writes to the same log at the info level, so you don’t need a console window.
Try it
Count how often players joined since the game started, and keep the count across hot reloads. Log the count on every join, and send the player a message like “You are visitor number 3.”
Show a solution
mod.persist.visitors = mod.persist.visitors or 0
cl.on("player.join", function(event)
mod.persist.visitors = mod.persist.visitors + 1
local n = mod.persist.visitors
cl.log("visitor %d: %s", n, event.player.name)
event.player:send_message("You are visitor number " .. n .. ".")
end)
Change the message text and save while the game runs: the next join uses the new text, and the count goes on from where it was.
Recap: clight run starts the game with your mod, saved scripts reload while it runs, mod.persist survives reloads, and cl.log writes to logs/latest.log.