Gameplay Testing

After some furious work trying to bring up the stability and playability of the game, Nick and I have had another play session with Tim and Nart of Witchbeam. The game only failed a couple of times, which was good news, although one of the resulting desyncs has been impossible to track down. I’ve added even more debugging in the hope of catching it the next time it happens.

We tried a few different maps this time, including Abyss, which sees the two teams facing each other across a bottomless canyon. Some adjustments need to be made to improve the pace of this map, but we enjoyed the variety after having played Vanilla – our basic test map – so many times.

We wrapped the night up with a few 2 vs 2 co-op matches. While somewhat chaotic, it’s an incredible amount of fun. The improved replay features were used heavily to pin-point blame.

Script Serialisation

‘Serialisation’ is the computer science term for saving a representation to a serial format, such as a file on disk. ‘Deserialisation’ is the opposite problem; turning that serial, or linear, representation back into a live object that the software can manipulate in memory.

The artificial intelligence (AI) operates in a Lua script. The script has state, which tracks various objects of the game, as well as internal goals, such as the shape and composition of the future fort the AI intends to build (as recorded by a human prior to the game), and data to help the AI seem more human, such as delays and weapon inaccuracies.

Some of this doesn’t change over the game, but the data that does needs to be serialised into the replay keyframes so that the deserialisation will reproduce the AI at that point in time. If it doesn’t succeed, decisions made by the AI afterwards will deviate, and the replay will desync. That is, it won’t be a faithful reproduction of the game.

LuaPlus, the package I am using to integrate Lua into the game, has some additional functionality that makes this quite straight forward. I can call a function DumpObject that will write the relevant contents of the Lua state directly to the memory file I keep to contain the replay. I only do this for a subset of the variables, the ones that are changed by the AI script, to save space in the replay file.

This has some consequences for the AI script. All dynamic data must be stored in a certain table, and function or table pointers can’t be stored, as these are problematic to restore. Instead names of functions or tables are stored and these are looked up when needed.

The process of implementing this feature brought to light various bugs and unanticipated requirements, so it took longer than expected, but the concept works as initially envisioned.

There is another script that implements missions. A simple example is a mission that counts down to zero, at which time one team wins if they haven’t lost their reactor. Another more complicated example is a tutorial or a single player mission with specific objectives. These scripts are serialised in the same way. An outgrowth of this will be a save game feature, so games can be continued later.

The end result is that I can now play a game against the AI, or a mission, see an instant replay when I win or lose, and watch it back later, seeking to any point at any time. Because AI and missions can co-exist in any combination with any number of players, large online games can be enjoyed again or analysed.