How To Cog - Chapter 3.4 - Debugging

This tutorial covers the basic decision process of debugging something you've written. Once you get used to it, this will all seem pretty easy, but it takes most editors a while to learn how to debug their code. Too many beginners go straight to a message board or chat room because they don't know how to debug properly.

Print Verbs

The Print Verbs that JK provides are probably the most helpful debugging tool. If you've just typed out a small cog and nothing's happening in the game (the most common problem you'll face), put a print statement in the startup handler to make sure your cog is actually running. You'll need to use a sleep of one or two seconds depending on how long your machine takes to start the game after startup. Although Print() is the most common, there's a ton of printing verbs available to you.

Concatenation means to link or join together. In programming, concatenation (or just concat for short) is a term for adding strings together. In some languages, the '+' operator is overloaded (given another purpose) to concatenate strings. So you'd be able to write: stringVar = 'word1' + 'word2'; And stringVar would have a value of "word1word2".

Cog is not that fortuneate, but the concat verbs provide the same functionality. JK sets up a special string for these verbs to use. This string is not cleared unless you clear it, so the first thing you do when you concat strings is to clear whatever was left over from the last concat operation. To do that, use JKStringClear().

You can concat ASCII strings, flexes, ints, players' names, UNI strings, spaces, and vectors. ASCII strings are enclosed by double-quotes. For example:

  JKStringConcatASCIIString("yourTextHere"); 

For flexes and integers, you can either concat them the same way the regular print verbs display them, or you can use the ConcatFormatted verbs with format specifiers - these give you a lot of options for customizing how the numerals will appear. Format specifiers aren't part of Cog, they're part of Visual C++. The Print Verbs notes includes a complete description of the specifiers that Cog can use. The best use of these specifiers is to output numbers in hexadecimal (very useful for displaying flags).

The Concat verbs are the only way to retreive a player's name, but there's not much you can do with it except to print it. This isn't helpful for debugging - just for displaying scores. Concatting a UNI string is pretty useless because there's already a UNI print verb, and there's not any reason to want to concat a predefined string.

Concatting a space is also pretty useless because you can do that with the ConcatASCII verb. Concatting a vector is very usefull for debugging when you need to look at several vectors at once. If you just printed them out, they'd all appear in the same column and you wouldn't be able to tell them apart. But with the concat verbs, you can print as many as you need in one line.

Results of Print

So your cog's not working right, and you've just tried putting a print in the startup handler. If nothing printed (and you had a sleep of at least two seconds), then first make sure you correctly declared the startup message. If that's there, then you need to make sure that your cog is being loaded. If your cog is correctly listed in either the items.dat or the cogs section of a JKL, then you must have a syntax error.

The most common syntax error is a missing semicolon somwhere in your cog. Or maybe you've got a missing code or end keyword. Sometimes the error will be subtle - like having spaces in the flags assignment - the cog looks fine, but when JK parsed (read through) the cog, it found something it didn't expect and this caused it to ignore the cog.

If the print did work, then you know the cog is being loaded and your syntax is (for the most part) correct. If you see the print appear twice, then your cog is being loaded twice - check for duplicate entries if this isn't what you want.

Once you're done with that basic troubleshooting step, how to proceed depends on your situation. Say you have a certain message handler that's not running. You've put a print in that handler but nothing's printing. It's probably an event message for an event that's not occuring (or the cog is not associated with the object(s) it happened to).

If you know for a fact that the event did occur to an associated object, then the cog may be blocked from receiving that message. Mask flags are the most common cause (the defaults are very restrictive). In a class or inventory cog, you can do nothing; the only way to change mask flags is to use a level cog and the mask symbol extension to allow any sourceref for that event message.

The key to effectively solving problems with portions of your code not working, is to start by putting print commands at startup and work down to where the problem is. Once you find the point of no return in your code, check the surrounding syntax - you might have missed something easy like a semicolon right after an if statement. If it's not obvious then check your references for any comments on the verbs or keywords you're using. If you have questions about a verb or its syntax, most of the time it's a common enough question to be in a reference somewhere.

You should also use a syntax checker such as Parsec or Cogwriter. These programs are good at finding obvious problems such as missing semicolons. Parsec can check for a lot more than just syntax, and can be easily reconfigured. Before going to a message board with your problem, use a syntax checker.

Flow Control Errors

Flow control is a phrase that programmers use to describe how code execution flows down through your code. Sleeps, Calls, Pulses, Loops, and If-else statements are all methods to allow programmers to change the normal top-down flow control of a program or script. Normally, an event is received by a cog and code execution starts right after the label and ends when the return command is given.

A Sleep() pauses code execution, but it's a lot more complicated than that because JK has to remember where it stopped in the code and what it was doing at the time. In the Threads tutorial, this is gone over in a lot more depth, but basically you have a limit on how many sleeps you can have simultaneously. Putting a sleep in a pulse handler (which is always a bad idea) where the sleep is longer than the pulse interval will create too many threads. JK will try to run the pulse handler, but it already has too many paused code executions in memory. Timers do not have this problem and should be used instead of sleeps if possible.

Calls are similar to sleeps in that current code execution is paused while JK calls on another handler. If you nest one call inside another too many times and you hit the thread limit, the oldest thread will be deleted. So code execution will never resume in the handler that made the first call.

Loops, in all their different forms, have a block of code that's run and a condition that's evaluated to see if that code should be run again. If the condition is true, then the flow of code execution resumes at the beginning of the loop's code block or statement.

If statements, especially ones that are deeply nested with elses, can also be a source of errors if you don't use proper indentation. Flow control can be difficult to imagine as you're reading your code if you can't clearly see which else belongs to which if.

For errors like these, try reading through your code carefully following the code's flow. Keep track of what values the variables might have, and work out the operations. This is a good way to fix problems and prevent future bugs.


Previous: Encapsulation Up: How To Cog Next: Multiplayer
  • Create:
This page was last modified 16:21, 14 April 2006.   This page has been accessed 1,705 times.