Let’s see how long we can keep that technical article streak, as we slowly but courageously inch toward an engine comfortable enough to tell a story.
This week, I’d like to talk about an idea - it’s not new, but I like it. When making a game, it’s not unusual to go through what is called the edit compile link run cycle, conveniently illustrated here for comedic effect:
Now, since we’re using TypeScript, cf. weekly update 12, we can often skip the “link” step, and the “compile” step is more of a “transpile” step, but it still means restarting the game, there’s no hotswap of modules or anything to that effect.
And since we’re using ooc, which compiles to pretty readable C, we can use gdb, or lldb, and any of their front-ends to debug the ooc part of the code. But that’s not what I want to touch today.
Today is about… experimentation. Trying stuff out, that might make it into the final game (ah, the final game…). It’s about testing what-ifs, about changing the values and seeing what happens. Wouldn’t it be nice to be able to do that to a live game instance? To peek inside, change what’s running, without interruption?
If you’re using a brand name game engine like Impunity or Harpseal Engine, you might get all that for free. But out there in not-invented-here-land, we have to fight for it! Although, isn’t that half the fun?
A second window into our little world
Here’s what I’m thinking - the game, while running, already has much to display, so let’s not start adding overlays on top of it. Besides, even though the level editor is pretty neat, text input is still not that good in our in-game UI framework.
You know what platform is pretty good at text input, though? Web browsers!
Here’s what I have in mind:
And now maybe my weird article about adding an http server to a game makes a bit more sense to the you, the reader. By the way, the cover image for this blog post isn’t what we’re building — it’s the Lapis web console, that gave me inspiration for this tool.
So basically we need:
- To send that code over to the game’s http server via a POST request
- For the game to evaluate that code in a useful context
- To respond with the results of the code (if any)
Text input is the easy part. We can use the wonderful CodeMirror package to set up a code-highlighted editor in no time:
It even has a command system, so we can map Ctrl-Enter (and Cmd-Enter) to our send command.
I’m not going to bore you with the details, but it’s going to take a while to interface with their API. Let’s try it, I’ll be right back.
Hey that only took half an hour! Nevermind, had to spend a few more hours debugging threading/GC interactions. But now we can actually receive POST requests without crashing all the time.
Here’s a list of problems I encountered:
- microhttpd creates its own threads, and the Boehm garbage collector doesn’t know about them, I had to find out about manual registering and unregistering
- microhttpd’s USE_SELECT_INTERNALLY option fails in weird ways with the garbage collection, had to fall back to one thread per connection (which scales less, but we don’t care for that use case)
- microhttpd is tuned for performance, so it’s heavy on callbacks - for POST requests, it calls the data handler callback several time, first with only the headers, and then several times still with chunks of the POST upload data
- microhttpd provides a POST upload data processor, which involves two callbacks: one to consume the upload data, and one to iterate through the key/value pairs.
- the iteration callback can be called several times with the same key, and values should be concatenated
- the iteration callback contains an offset parameter, but it’s not an offset in the data given, it’s an offset in the overall data — hence I ended up with partial responses.
With all that settled, it’s time to move into evaluation.
Another thing of note is ‘this’ - one might think it would be bound to the object from which eval is called, but it’s not: it’s bound to the global value (e.g. window in a browser, but remember, it’s evaluated in Duktape, so it’s the global Duktape context).
var self = this;
before the val call is enough to work around this.
Since I’m running out of time to write this article, I went the simple way: the server responds with JSON, and all the inspector does is display that JSON as a string.
Ideally, the server would keep around a results array, and populate it with the arguments of calls to a ‘print’ function declared in this scope. When an object is passed, one would have to be careful about serializing it to JSON, as the game engine is full of cyclic structures that don’t play nice with ‘JSON.stringify’ out of the box. Cycle detection is relatively easy to implement, though, and a simple ad-hoc routine would be enough to work around that problem.
Finally, here’s a screenshot of a relatively fun test of our remote console:
That’s it for this week!
Stay tuned for more technical articles in the coming weeks, and don’t hesitate to follow us on Twitter and to share the articles you like. Thanks for reading!