Browser-based game inspector

Hey folks!

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:

  • A way for the user to input JavaScript code
  • 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)

Input

Text input is the easy part. We can use the wonderful CodeMirror package to set up a code-highlighted editor in no time:

full

It even has a command system, so we can map Ctrl-Enter (and Cmd-Enter) to our send command.

Send

Sending some data in a browser via an HTTP POST request is really not that hard, even in vanilla JavaScript. The hard part, for us, is to receive it. Since microhttpd is a GNU library, it makes everything complicated.

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.

Eval

Evaluating code is relatively straightforward. JavaScript has eval, which is evil, as everyone knows. But it’s exactly what we want to do. In order not to crash the whole game, we’ll wrap it in a try/catch block and return an error object with a stack trace if something goes wrong.

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).

A simple:

var self = this;

before the val call is enough to work around this.

Respond

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:

full

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!