Today’s update is a little lighter on text compared to the previous posts, as all the groundwork on which the main new feature was built has been done and documented previously. The feature I’ve been working on is looped live code, where ‘looped’ refers to replaying the same action replay over and over again, and ‘live code’ means modifying the game code while we’re doing the looping, Makes sense, right?
Looped live code
The first time I saw an implementation of looped live code was in Casey Muratori’s entertaining and informative Twitch/YouTube show Handmade Hero, where he codes a complete game from scratch, broadcasting every second of it. While I don’t always agree with Casey’s coding style, he’s a veteran in the game industry and has been working as a developer on Jonathan Blow’s upcoming and highly anticipated game The Witness, so he knows a thing or two about game development. Be sure to check out his YouTube archive of Handmade Hero episodes, and his blog to read his diary developing The Witness, if you are interested in game development!
Looped live code is not just a gimmick, but a very useful tool for prototyping new gameplay features or debugging existing gameplay. By looping some section of the game over and over again while tweaking or adding game logic, the effect on gameplay become immediately visible. Particularly hard sections can be tuned until they are just about right, bugs can be reliably reproduced, and the feature could be used to build a live level editor, the possibilities are limitless. So when I saw Casey Muratori’s implementation of looped live code in Handmade Hero, I decided I had to implement something similar into 2k14: The Game.
The thing I don’t really like about Casey’s looped live code implementation in Handmade Hero is that it’s a bit too ghetto to my taste, and that it can’t do the things I’d like to be able to do with 2K14: The Game. Which means: updating game code running on an iOS device or the simulator, without requiring recompilation. Looped live code in Handmade Hero works by basically writing a memory dump of the complete game state to disk along with the inputs during received while recording. When the replay is (re-) started, all game state is read back from the replay file and overwritten in memory, down to the last bit. To allow live code updates, Handmade Hero moves all game code into a DLL, which is reloaded automatically whenever it is recompiled, by means of a file timestamp check inside the main game loop.
The advantage of Handmade Hero’s method is simplicity. The disadvantages are multiple, but I’ll just list the three most relevant for our purposes: First of all the replay file has to be huge, as it has to cover the total memory footprint of the game. Second, the replay file will break, possibly crashing the game if it was running, if the in-memory layout of the game state changes, which is every time some field of a structure is changed, moved, added or removed. Third, the live code updating requires recompilation and dynamic linking. All of these three limitations are prohibitive for an iOS application, but fortunately, now that we have a scripting engine, we can do better :-)
The first thing that needed to get sorted out was looped replays. Everything was already in place to implement this feature, so the implementation effort was minimal. Whenever gameplay recording starts, a snapshot of the current game state is made, which can be used to reproduce the start state of the replay. Note that this is not a memory dump like in Handmade Hero, but just the minimum amount of information required to re-create the game, planet, and entities at the time recording started. You could view it as a ‘recipe’ or a ‘collection of parameters’ from which the start state can be reproduced. While recording, all gameplay inputs are serialized, and after recording stops the start state and inputs are bundled together and written to a file.
Replaying is just a matter of re-creating the game, planet and entities from the start state snapshot, and simulating the inputs as if someone was playing the game. All of this has been working for a while, including storing/re-creating Lua VM state when recording/replaying. The single thing missing was to reset game state to the start-state snapshot whenever the replay ended, and ‘rewinding’ the captured inputs. Nothing groundbreaking.
To make looped replays a little more interesting, I added a ‘start/stop recording’ button to the in-game controls. This allows recording gameplay at any point in the game, instead of just from the beginning. To maximize the usefulness for rapid prototyping or debugging the game, this is a must-have feature, as the bit of gameplay you’d like to modify or debug might only get activated after a long sequence of actions.
Similar to the looped replay feature, live code updating required minimal
effort, thanks to all the work that went into the scripting engine. One of the
main advantages of scripting languages is that it’s almost always trivial to
execute arbitrary code fragments inside an existing VM, without requiring
offline compilation: just feed the fragment to the scripting engine as a
string, which will usually compile it on-line to some kind of bytecode, and
inject this bytecode into the VM. Lua is no different, it’s native API has
functions like ‘
luaL_dostring which does exactly this.
The missing piece of the puzzle is how to get new code fragments into a game that’s already running. Since we cannot directly access or modify on-device storage, we need a way to push code to the device using some alternative medium. Like, for example, the network ;-)
The solution may sound a lot more complicated than it actually is in practice:
just embed a web server into the application! The webserver can listen for
incoming POST requests, and whenever it receives one, interpret the request
body as Lua code and source it into the running VM using
Obviously we don’t need a full-blown webserver like Apache to push some code fragments, just something that listens at a port and parses HTTP requests. After some searching around I found the terrific GCDWebServer, which is actually an already very capable web-server that supports many things we don’t need, but at the same time is very lightweight and ridiculously easy to integrate. The whole process of downloading GCDWebServer, adding it to the project, and starting the webserver when the game starts, only took about 10 minutes, it’s really that easy. GCDWebserver makes great use of modern Objective-C and Cocoa features, which means adding handler code to process incoming requests can be done using an inline code block occupying a single line of code. Kudos and thanks to GCDWebServer’s author, Pierre-Olivier Latour, aka swisspol!
GCDWebServer uses Grand Central Dispatch to handle incoming requests
on dedicated GCD threads, which means some care was needed to prevent concurrent
access to the Lua VM, which is not thread-safe. I solved this by queuing up
fragments using a locked
NSArray, which is emptied and processed inside the
game update loop. Code fragments are stored in a dictionary, using a key
derived from the URL that was used to push them, so they can be made ‘sticky’
and overwritten with new versions. Whenever the replay ends and is re-started
the code fragments for each key are re-sourced, to override entity state that
may have been restored from the replay start state snapshot.
To simplify pushing code fragments to a replay running on an actual device, the level select screen now displays the device IP address and post URL. POST-ing fragments to this URL can be done using an HTTP tool like cURL, which is installed by default on any decent OS you would want to use for development, ie: any Linux distro or other Unix-based OS, and Mac OS X. But I’m sure getting cURL or something similar on Windows should be a breeze too, just don’t ask me because I wouldn’t know ;-)
The best way to explain the usefulness of looped live code is using a video. In the video below, you can see the feature in action. I pre-recorded a bit of gameplay on my iPhone, and copied the replay file to the simulator to capture the video.
Note: the video is best watched full-screen at HD quality on desktops, or inside the YouTube player on mobile. In the embedded player the code pushed to the game is almost unreadable.
Note 2: there’s some brightness flickering visible in the video, which is some weird software problem on my computer but unrelated to the game ;-)
While the replay is looping in the simulator, a few simple code modifications
are pushed to the game. First I change the planet tint color, by getting the
planet through the global variable
game, which is set automatically when the
game starts. The second code fragment pushes a new
step method for the
entity, which will pulsate the size of bullets using a sine function that takes
the elapsed game time as an input. The last code update changes the planet
gravity by adding a non-zero x-component, which will cause the player ship
to veer left.
While these may not be the most advanced use cases of the looped live code feature, you can imagine that when we start to add some actual gameplay features to 2k14: The Game, looped live code will be an extremely useful tool to try, tweak and debug the new features, as it removes the need to rebuild, deploy and restart the application whenever some piece of gameplay logic is changed.
I haven’t decided what to work on next. Maybe I’ll refactor the way levels (planets) are represented to allow more interesting planets, for example adding support for switches, moving surface edges, etc. But I also feel like doing some graphics stuff on the side again, maybe some support for sprite animations, explosions, or configurable sprite color palettes.
The looped live code feature needs a little cleanup as well, one thing I don’t
like about it right now is that the code fragments are stored inside the
K14ScriptableGame instance, which sources them every time the game is reset
from a snapshot. Since we have to source all pushed code fragments on every
reset anyway, maybe it’s a better idea to store the fragments with the view
controller, and create a whole new
K14ScriptableGame instance with a fresh
Lua VM every time the replay is restarted.
Implementing looped replays, integrating the webserver, and supporting live
code updates together took a little over 2 hours. Unfortunately I did spend
over 5 hours debugging a stupid bug in my Lua VM interface class, which
had a Lua stack underflow bug that resulted in really weird random crashes
that were almost impossible to trace to the actual code problem (decoding
a Lua table value to an
NSDictionary incorrectly popped one item too
many from the Lua stack). This brings the total implementation time to
about 238 hours. Native code SLOC count went up by 191 to 5904, a not
insignificant amount of which went into boilerplate initializers I had to
add to silence XCode 7, which has a much higher warning level when you
NS_DESIGNATED_INITIALIZER (which is a good thing, by the way).
Lua SLOC count increased by a grand total of 8, and now sits at 649.