Scripting Helpers is winding down operations and is now read-only. More info→
← Blog Home

An essential programming skill: Debugging

You might have noticed that writing code is much harder than writing code that works. After you fixed every error and your script started running, you noticed that the things that happened did not match your expectations. In order to fix this, you had to dive in the code in order to find and repair those errors: debugging.

A fun fact: The term bug actually comes from a real bug. Back in 1947, when computers had the size of your living room, the operators of this computer were trying to fix a malfunction in their calculator. After a while one operator found a moth on one of the computer panels. This bug was the cause of the problem!

Mark II computer

I always thought that learning how to use a language was the hardest part. So do most beginner programmers. I figured out, though, that this is not the case. The hardest part of programming is to debug. If you ever try to learn another language you have to go through the „pain” of learning that language (the syntax) again, but you don’t have to learn how to debug anymore. Debugging is a skill which you can train. The more you program, the better you will get from it. Every scripting helper is actually also helping to debug, giving the right tools to figure out whats wrong!

The more you program, the better you will get at debugging. You will also find ways to prevent bugs, or to find bugs faster. This is a kind of „learn it the hard way” process, at least, in my case it was. I never gave my variables nice names and made functions which could do quite random things. Via the hard way I learned that giving variables names with information in it and functions which had strict behaviour (you input a table, you get a table output, the keys represent x, the values represent y) which I commented at the top of functions. This is really handy if you reuse this code later and need to find a bug.

The output The best tool to debug is your output. For starters, this is located here:

Location of output

Please note that I am using the the Mac version of Roblox Studio - the Windows version has it on the same place.

The output window will show up:

Output window

Every time you call the print function in one of your scripts, the values (which are converted to strings first by tostring) will be written to this output window. You can use this to inspect your variables everywhere in your scripts. The best practice for stubborn bugs is to limit your problem. I distinguish 5 bug types: syntax "bugs" (errors), logical bugs, API bugs , platform bugs and Roblox bugs. The first one is the easiest to trigger (your scripts won’t run because you didn’t supply valid Lua) and you will need to fix the first and second type of bugs the most.

What I normally do is this:

Syntax Error

If you got a syntax error, it’s obivious: you didn’t supply valid Lua. The output (in red and blue) will tell you exactly where the error happened and where it came from (the stack trace). This makes those errors the easiest to debug.

API-related bug

Then be sure that the bug is not API-related. If you see that a block (which has a BodyForce in it, for example) is not moving, while you expect it to move, then check if not some API property is blocking it. For example, you may have set the Anchored property of that block to true! You could also trigger the wrong method (think of resizing a GUI while you wanted to reposition it!).

Logical bug

If you didn’t fix the bug yet, it’s in most cases a logical bug. These happen because Lua doesn’t know your intention - and your logic in the code is "off" if you compare it to your expectation. Those could as happen as fast as subtracting something while you really want to add something! Scripting helper question answerers also don’t know your intentions! If you post code with „help me to fix” then in most cases no one can answer it, as they don’t know what you want!

What you should do with logical bugs is try to limit your error. If you know that one of your trees isn’t regrowing for example, you should figure out if you called the „regrow function” every time this is necessary (such as when the tree is removed!). It could also be that something in the regrow code is preventing it to regrow. For example, in order to prevent a whole script from crashing, I sometimes do this:

function Regrow(TreeParent)
    if not TreeParent then -- TreeParent is nil or false!?
        return
    else
        --regrow code
    end
end

You can see here that when I don’t supply a TreeParent, then the actual regrow code will not run! Aha! Let’s figure out then if the Regrow function runs when I want it to happen (such as removing a tree from Workspace - we want a new tree to grow!).

function Regrow(TreeParent)
    if not TreeParent then -- TreeParent is nil or false!?
        print(„The tree Regrow funciton is running, but no TreeParent has been supplied!”)
        return
    else
        --regrow code
    end
end

You update your code and run this - then we remove a tree and see that the message is being written to the output! Aha! Actually, this is how I should have written the code in first place, as it would have prevenented me looking for that code and immediately showing that something is wrong! What would be even better currently is to actually make it error (while I used it in the first place to NOTmake it error, I know!) so we can use the stack trace to immediately find out what’s going wrong - and moreover, where! The new code would be:

function Regrow(TreeParent)
    if not TreeParent then -- TreeParent is nil or false!?
        error(„The tree Regrow funciton is running, but no TreeParent has been supplied!”)
        return
    else
        --regrow code
    end
end

This will print exactly where the Regrow function has been called. You can then go there and either figure out you forgot to pass an argument (just calling Regrow()) or that the argument you provided is nil. In most cases you figure out that this happened:

local Tree = game.Workspace.Tree
Regrow(tree) — notice the lower case

This is then an easy fix. In some cases you also have to look even more. Try using the search option to figure out if and where that variable has been defined. Then check the statement which has defined the variable. Using this and handy print statements on handy locations you will eventually track down the bug - even the most stubborn ones - and slap it in the face to kill it. Your code will run like a charm!

Debug code

On the picture above you can find a way to log stuff. You will get clean output with DebugMode and VerboseMode set to false (only if things go really wrong). If you want more info you set both to true (or one of them). This is a small example on how to log stuff in your game. You could even override the print function!

I once did all of above and still could not find the bug. I spend a whole night on fixing it. What happened? When I did Test Server for my game, around 30 trees were spawned. However, on an online game server, around 160 trees were spawned! How in the world did that happen..? I tried limiting everything in my code down to figure the problem. It appeared to be a problem which had to do with math.random - because I randomized the amount of trees which I had to spawn per brick. I didn’t get it. The input values were constant, but for some kind of reason the server got another output of this math.random call than the output on the server.

I finally figured it out! I used a Mac with test server. Apparently, when asking some Lua users which had some really good knowledge on the technics behind the language, math.random called with non-whole-numbers had „undefined behaviour”. I knew that math.random(3.4, 3.6) wasn’t a valid statement, but I thought that the function would round them. It appears that on the operating system where roblox servers run on, the values returned in above statement were either 3 or 4, while on my mac it was always 3! That’s why I decided to classify this as a platform bug, as it appears to happen on one platform and not on the other. These ones are extremely annoying to find.

bsod

Some users even reported blue screens of death due to a possible roblox bug!

The last one is a bug which you cannot fix. Roblox itself has of course also bugs! For example, many users knew that you could shut down servers by inserting a ManualSurfaceJointInstance into game.Workspace. This was a Roblox bug: it was not supposed to happen. You can figure out where these bugs happen using above method. Try to figure out on what line the bug is happening, and think of what is causing it. Then you can report your bug to the „official” bug tracker.

The last thing I want to tell is that we finally got a way to debug in servers! You would expect that Test Server mimics what roblox servers do, but this isn’t always the case. Moreover, if you test, you normally play around a minute on your game testing if every feature works, and then start coding new features. On game servers, people can play for hours - and these can trigger bugs which you didn’t encounter before. Before the in-game logging update, we didn’t have a really good way to debug in servers. There were people who created in-game outputs, but we couldn’t get the errors yet (which are the most important in debugging). At some point we got the ScriptContext.Error event, which we could implement in the outputs to view these errors. But now, every roblox game has in-game output!

Blog

To reach it, hit F9 in any game! In your own games you can even retrieve server logs. Please note that these outputs are not real-time, but are actually logged. Thanks, roblox developers! This tool is amazing!

Happy debugging!

Posted in Scripting Tips

Commentary

Leave a Comment

trogyssy says: June 7, 2014
first sentence: "code *that* works"
jobro13 says: June 18, 2014
Changed!
bbissell says: September 2, 2014
I'm pretty sure the dev log is real-time. I dont know if this is an update or if it has always been this way, but I know that it currently is real-time.
parkderp1 says: March 26, 2015
The F9 trick is really helpful, Thanks!