Monday, 18 September 2017

Debugging your TTS mods with console++ : Functions

Don't just jump in here: start at the beginning!  Once you've loaded the module in Tabletop Simulator remember to switch to the Game tab and turn on command mode with '>'.

In the previous posts we've seen how we can display variables with 'ls' (or 'ls -v') and objects with 'ls -o'; the final option available is to display functions with 'ls -f'.

Here you can see the functions defined in the example mod.  We can do more than just display them, however; we can also call them.  Type 'call dice_total', then roll the dice and call it again.

What if we want to store the value a function outputs?  console++ defines a special path for the last returned value: the '~' character.  You can see this if you display it with 'ls ~', and all other commands can use it like they would any other path; this includes 'add' and 'set'.  Add a variable called total using the '~' path and check it's set to the right value:

The 'add' and 'set' commands can also be used to store a result directly, by calling them with no value argument before the 'call' command.  If you do so the the next 'call' command will store its result where you have specified.  Roll the dice again and then try it by typing 'set total' and then 'call dice_total':

Note that console++ also has a short-cut command to display the last result: simply type the result character on its own to display its contents: '~'

We can pass parameters to functions just as you would expect, by adding them to the 'call' command.  Let's use the is_face_up function on one of the cards.  Remembers, the card objects are stores in the cards table; you can see them by typing 'ls -o cards'.
Type 'call is_face_up cards/2' to call is_face_up on the 2 card, then flip the card and issue the command again (use up-arrow to get the last command typed instead of typing it again).

Note how when we 'ls -f' we see only the functions defined in the mod; we don't see all the built-in Lua functions.  That's not because they are not present; it's because console++ hides them under the 'sys' label.  If you do an 'ls' you will see two things at the bottom of the tables that look like tables, except they are in a salmon colour instead of yellow; these are labels used to hide globals to keep things tidy.  You can see what they are hiding by calling 'ls' on them;
try it now: 'ls sys -f'

At the point in the Lua code where you #include console++ it automatically scans all the globals and hides them under the `sys` label, thus keeping your mod easy to interact with in the console.  All these things still exist in the root table, though they may also be accessed via the label they are hidden under. i.e. 'ls /math' and 'ls /sys/math' are the same thing:

You can also create your own labels to hide things under by calling console.hide_globals in the mod's Lua code.  Type 'ls' and you'll see that as well as the 'sys' label there's also a 'guids' label; if you look at the end of the GUIDS block in the Lua code you'll see the command that created it; whenever you call console.hide_globals it will scan for global variables/functions etc and hide them under the label you specify (provided they haven't already been hidden under another label).

To come back to the point: all the built-in functions are available should you wish to call them.  For example, we can get a color using stringColorToRGB then display the RGB values:
'call stringColorToRGB Blue' then '~':

We can then store it in a variable and use it to highlight an object:
'add blue ~' then 'call /decks/b/highlightOn blue 100'

Thursday, 7 September 2017

Debugging your TTS mods with console++ : Manipulating Variables

If you haven't read it then you should check out the first installment of this series, which goes over how to get set up.

Last time we saw how to look at our program's structure.  This time we'll see how we can manipulate it in real-time by changing and adding variables.

First things first though; last time we were entering commands by typing the command symbol ('>') and then the command.  Let's shortcut past that by entering command mode.  You can type '>cmd' at the prompt to enter command mode, and when in command mode you can type 'exit' to leave it.  When you are in command mode the console will interpret all your inputs as commands, so you no longer need to prefix everything with '>'.
But we're not going to type those!  Instead, an even easier way to enter and exit command mode is to just type the command character on it's own; if you hit '>' then enter it will toggle you in and out of command mode.  Try it now:

After I entered command mode I typed 'cd cards' (just like last time), and you can see that while in command mode any commands you perform will end with a prompt which includes our current path.

OK, so now we can simply type commands without a prefix, let's try a new one out: the 'tgl' (or 'toggle') command flips a boolean.  Let's go back to the root table with 'cd /' then remind ourselves what is there with 'ls'.

At the bottom you can see the four global variables in the mod, and the bottom one is a boolean, currently set to false.  If we look at the code we can see what it does:

update_cards acts as a sentinel on the onUpdate event. Let's turn it on so the mod actually does what it's supposed to do.  Type: 'tgl update_cards' and see what happens.

Well, that isn't good.  The mod clearly doesn't work right, and worse, because the bug is in the onUpdate event it is spamming errors every time it engages.  Investigating will be impossible while the console is being filled with error messages, so turn it off again: in the console hit up-arrow to get the previous command and enter to execute it; this will toggle the update_cards boolean back off, disabling the faulty onUpdate event code.  Note this technique; it's always a good idea to wrap timer-event driven code inside a boolean like this so that if things go wrong you can easily disable the code without aborting from the faulty session.

So what's the problem?  The error is on line 1542, so go to the global script in Atom and hit ctrl-g and enter 1542 to go to the offending line number.  The Atom plugin will take into account any #includes in the code, and will take you to the correct line, which in this case is the one that reads 'cards[count].flip()'.

The error is suggesting that we're trying to index a nil value, so either cards is nil, or the individual location of cards[count] is nil.  Let's have a look: type 'ls cards' at the console.
Nothing? Oh, the cards are game objects; try 'ls -o cards'.

There they are; cards is clearly not nil.  Let's look again at the code - what is the cards table supposed to do?  You can see in the onUpdate event it is looping over the numbers 2 to 10 - the card values on the table.  The table has been set up such that to access the 2 of Spades we use cards[2], to get the 3 of Diamonds we use cards[3], etc.  In the onUpdate event we are looping over each of the cards from 2 to 10 and possibly flipping them.  However, look at the 'ls' output: the cards are not indexed from 2 to 10, they are indexed from 1 to 9.  Looks like the table has been set up wrong! There is no cards[10] so when it tries to access it on the last iteration of the loop it doesn't find anything, thus the error message.
Before we jump back into the code to try to track this down, let's confirm that that is the problem; we'll add a card at that index and see if the code works.  To add a variable we use the 'add' command.  We'll set it to the card that's not being looped over: cards[1].
Type 'add cards/10 cards/1', and then 'ls cards' again.

You can see it has a card at index 10, and it has the same GUID as the one in index 1.  Turn on the code by toggling the boolean: 'tgl update_cards'. No errors - nice!  The update code is working, but our table is still set up wrong; all the indexes are off by one (as you can tell by the card 3 being face up, but the total of the dice only being 2).  Select the dice and hit 'r' to roll them.  The cards will update, but always to the wrong value.

We now know what to fix in the code though, and on investigation you can see the part that sets up the table:

This looks pretty straight-forward; the table is created with a null value in index 1, so that when it loops over the GUIDs of the cards it will add them to slot 2, 3, 4... etc. all the way up to 10.  However, if you are more familiar with Lua than the programmer of this code you will know that nil does not behave just like null does in other languages; in Lua when you assign nil you are actually deleting that entry, so this code is no different from typing 'cards = {}'... it makes an empty table.  The fix should be simple: change the line to 'cards = {'dummy'}' and hit Save And Play.  Now we can go back into command mode with '>' and type 'ls -o cards' to see that they are correctly indexed, 2-10.  (An exercise to the reader: where is the 'dummy' card?)

Toggle the update_cards variable to turn on the event code.  No errors, and the card being displayed matches the total of the dice!  Select the dice and hit 'r' to roll them, and you can watch the cards flip to match.

It feels a bit clunky though; the cards are updating very slowly.  Type 'ls' again to see what variables we have to play with: sure enough, there's a 'check_delay' number set to 1.5 seconds.  Let's set it to a shorter delay using the 'set' command.  Type 'set check_delay 0.3' then select the dice and roll them.  Much more responsive!  If you were writing this code you could easily play with varying values for check_delay until you found the one you liked the best, and then amend the code afterwards to match.

That covers the basics of manipulating variables in the run-time environment.  You can see how we can use it to debug our code in a controlled manner, and also to help in refining control variables. We've now looked at simple variables and objects; next time we'll look at the third of the triad: functions.

Monday, 4 September 2017

Debugging your TTS mods with console++ : Introduction

The typical cycle for debugging a game in Tabletop Simulator is to play it, wait for an error message to emerge, use that error message to find the problem, write a potential fix, then start again.  This is less than ideal in a lot of ways: the error message can be a good clue for what the problem is, but sometimes it can be extremely vague (sometimes it doesn't even have a line number!).  Potentially more time-consuming is that when your game is getting fairly well developed the bugs become hidden further in; it may take a lot of setup to get to the specific circumstances that trigger the bug.  Typically this means that once you think you've written a fix you then need to play the game out exactly the same was as before, coordinating any playtesters to do what they did before, until the bug manifests again... or does not, and is potentially fixed - but how do you know for sure?

console++ gives you live access to your mod's structure, treating it as if it were a file system.  It lets you examine your variables, functions and objects, and also lets you update them, all without leaving the game.

To follow along to this tutorial you need to get console++ from the link above and subscribe to the example mod on the Steam Workshop.  Once you've loaded it into Tabletop Simulator, save it to your own hard disk so you can save your edits.

Source code is visible here.
This post will cover navigating and displaying your program using the two basic commands in console++: 'ls' and 'cd'.  As a quick aside, I'll be using the unix-like command names during this tutorial ('ls'), but console++ includes DOS-like alternatives, so if you prefer DOS you can use those instead.  In this case, anywhere I say 'ls' you can type 'dir' instead.

What does 'ls' do?  Well, we could ask for help on it, but lets do the impetuous thing and just type it into the console; make sure you are on the Game tab of the console, not the Global tab it defaults to (also it would be a good idea to hit the cog and disable autohide if you have it turned on).

So, hit enter, type 'ls' and hit enter again.  Congrats, you just said 'ls'; the console is just the chat window after all, and you didn't tell it you wanted to use a command!  To use a command you type the prefix '>' before it, so lets try again, typing '>ls'.  Much more interesting, and if you looked at the source file above it should look a little familiar:

You can see the variables we defined in the source are there; the 'ls' command lists all the tables and variables in your current location.  Right now we're in the root of our program ('/') which is the global level.  You can see at the top of the listing some labels in yellow: these are tables.  Let's see what's in them; type '>ls decks'. Nothing there?  That's because by default the 'ls' command only displays tables and basic variables (numbers, strings and booleans); to get it to show game objects we need to use the '-o' option.  Try '>ls decks -o' and you'll see:

There are our deck objects: table entries a, b, and c.  We can see their GUIDs listed next to them; right click a deck on the table and check the Scripting submenu to see it matches up.

We can refer to each of these decks by their path: if you type '>ls decks/a' you'll get the listing for only deck a.  Now, if we were debugging something to do with these decks, and were going to type their names a lot it would be tedious to have to keep specifying their location.  Instead we can change our current location to suit our needs, with the 'cd' command.  Type '>cd decks' and then '>ls -o'.  As you can see we have changed the table we are in; we can now refer to the decks simply by their letters: '>ls a'.  Note that when we specify an exact path we do not need the '-o' option.

Paths behave just as you would expect for a file-system; you can specify relative paths from your current location or absolute paths from the root table (by starting your path with a '/') You can refer to a parent table by using '..' and your current table with '.'

Let's go back to the root table: type either '>cd ..' to go up one level (we are only one level below the root) or '>cd /'.  This example mod is clearly very small and simple, but you can imagine in a big game you will have lots of variables and tables; simply typing '>ls' and scrolling up and down looking for things could be cumbersome.  In that situation it would be good to use a wildcard to trim down what you're looking at: try '>ls c*'

That's the basics of navigating and examining your program from within TTS with console++, except to return to what was said at the top of the post about asking for help: you can type '>help' or more simply '>?' to get help on any command, and the commands with more complicated options will also support a '-?' option to provide more detailed information.  If you type '>? ls' you'll see it has a few options we haven't tried yet.  To wrap up lets try '>ls -ar' ...

Next time we'll look at actually interacting with your program by modifying variables in real time.