Debugging#
When a place does not do what you meant, you need to see what it actually did. Moud gives you six
ways to look: printing to the log, drawing shapes in the world, watching values on the screen,
measuring what queries and scripts cost, timing each render pass, and the editor's panels. Most of them hang off
game.debug, which exists on both sides.
print#
print writes to the game log (logs/latest.log), marked [server] or [client].
The mark tells you which side ran the line, which is usually the first thing you want to know when a script exists on both sides.
Drawing#
Drawing puts a shape or a label over the world so you can see a position, a direction or a box that your code is working with. Each call takes a number of seconds to stay up, and draws for one frame when you leave the time out. Called on the server, they show on every client.
local d = game.debug
d:drawLine(from, to, color(1, 0, 0), 2)
d:drawRay(from, direction, color(0, 1, 0), 2)
d:drawBox(part.worldCframe, part.size, nil, 2)
d:drawSphere(position, 3)
d:drawPoint(position)
d:label(position, "spawn", color(1, 1, 1), 5)
d:clear()drawLinegoes between two positions;drawRaygoes from a position along a direction, which is the one that matches what you handedworld:raycast.drawBoxtakes a frame and a size, so passingpart.worldCframeandpart.sizedraws exactly where a part is, rotation included. Passingnilfor the colour uses the default.labelwrites text in the world at a position, for naming the thing you just drew.clearremoves everything currently drawn, without waiting for the times to run out.
Drawing from a renderStepped or stepped handler with no time given gives you a shape that follows
the thing every frame, because it is drawn again each time.
Watching values#
watch puts a name and a value in a live list in the top right of the screen. Call it every tick
with the same name and the row updates in place rather than scrolling past you, which is what print
would do:
game.stepped:connect(function()
game.debug:watch("players", game.players:count())
end)Values watched on the server are marked (server), so a name watched from both sides shows twice and
you can see them disagree. Reloading the place empties the list and wipes what was drawn, so a name
your new code no longer watches does not linger.
What queries cost#
local stats = game.debug:queryStats() -- { queries, partsTested } since the last callqueries is how many casts and overlaps ran, and partsTested is how many parts they looked at
between them. Both count from the last time you called it, so calling it on a timer gives you a rate.
A partsTested that climbs while queries stays flat means each query is reaching further than you
meant. See Queries.
Profile#
profile tells you how long each script's handlers and each callback took since the last call,
slowest first:
task.every(5, function()
for where, t in game.debug:profile() do
print(where, t.milliseconds, t.calls, t.worst)
end
end)Each row gives you milliseconds spent in total, calls as how many times it ran, and worst as
the slowest single call. A high total with many calls is different work from a high total caused by
one slow call, and worst is what tells them apart.
main is the place's own script; a Script instance shows under its name; a callback shows as
Class.name, like TextChannel.shouldDeliver.
Frame times#
F8 (rebindable in Controls, under Moud) shows how long each render pass takes, in the top left of the screen, most expensive first. Each row is one pass with its CPU and GPU time in milliseconds, averaged over the last few frames, and two bars for its share of the frame: blue for the CPU, orange for the GPU. The top line is the whole frame and the frame rate.
-- client
game.debug:showPassTimes()
task.every(5, function()
for pass, t in game.debug:passTimes() do
print(pass, t.stage, t.cpu, t.gpu)
end
end)showPassTimesturns the same list on from a script, andshowPassTimes(false)turns it off.passTimesreturns every pass that ran in the last second:cpuis the time spent sending its work,gpuis the time the graphics card spent running it, andstageis where in the frame it runs, likegeometry,lightingorpost, orotherfor work outside the renderer such as scripts.gpuis missing for work that never touches the graphics card.
The two times answer different questions. A pass with a small cpu and a large gpu, like bloom
on a big screen, is the graphics card's work; a large cpu means the pass is busy preparing what it
draws. The GPU times arrive a few frames late, because they are read only once the card has finished
the frame, so the first call returns no gpu values yet.
The passes you will see most:
| Pass | What it draws |
|---|---|
Shadow Map |
the sun's shadows |
Deferred Lighting |
lights on parts and models |
Instanced Mesh (GBuffer), Instanced Mesh (World) |
parts |
Models |
mesh parts |
SSAO, SSGI, SSR |
ambient occlusion, bounced light and reflections |
Bloom, Color Grade, TAA |
the post effects |
HUD Surfaces |
every ScreenGui, chat and this list itself |
moud sky |
the Sky |
moud effects |
particles, beams, trails, ropes and rain |
moud screen effects |
ScreenEffects |
moud highlights |
Highlights |
moud editor selection |
the editor's selection outline |
moud viewports |
every ViewportFrame |
moud client scripts |
every client script's handlers and tasks for the frame |
Nothing is timed while nobody is looking: timing starts when the list is shown or passTimes is
called, and stops two seconds after the last call. With it off the passes run untimed and nothing is
asked of the graphics card. The same list, with the stage column, is in the editor under Window → Renderer
Tools → Info → Profiler.
Output and the inspector tabs#
Right Shift opens the editor over the running place. Its Output panel lists prints, script errors and warnings, with a filter for each and a link to the file and line of each error. Finding bugs explains how to read what it says.
Window → Renderer Tools opens the inspector. Its moud tabs show what the engine currently thinks:
- place counts the parts in the tree and the ones drawn, the collision boxes, your own body and the script's sleeping tasks, and lists the most recent errors.
- tree shows the instance tree as this client holds it.
- mixins lists every hook a place put on the game, with calls and time per second, errors, and a switch to turn one off (see mixins).
These are the quickest way to tell whether your place is wrong or the engine is. If your script raised, the error is in Output; if it did not, what the renderer and the physics report tells you where the value actually went.
Seeing collisions#
F7 (rebindable in Controls, under Moud) draws what the physics collides against, over the world. Each colour is a different kind of collider, so the colour tells you which path the physics took for that part:
| Colour | What it is |
|---|---|
| green | a part's box exactly as the physics received it this tick |
| green triangles | the faces a shaped part collides as |
| cyan | a turned part colliding as its real turned box |
| orange | a turned part colliding as the upright box around it, because turned collision is unavailable |
| grey | a part with collides = false |
| white | the collision shapes of blocks within 5 blocks of you |
| yellow | every body's capsule: players, mobs and the characters a place walks |
Orange is the one to look for: a part that should collide at an angle and shows orange is being collided against as an upright box, which is why something slides off it or catches on nothing.
The top right says how many boxes, shape faces, turned parts, ghosts, block boxes and bodies are within 40 blocks. It is drawn over everything, walls included, so you can see a collider through the floor it sits under.
