Skip to main content

Extending Fly.io's Distributed Turn-Based Game System, Part 2: GameBox

· 4 min read

In Part I, we wrote a special Elixir GenServer that allows us to replace any GenServer in our Elixir apps with an Extism Plug-in. Our goal was to build a version of this turn-based game system that allows users of the platform to create and upload their own games.

Today we’re announcing the result, GameBox!

GameBox Screenshot

GameBox

GameBox is hosted on Fly.io and you can start creating and uploading games today. You can write your game in any language where we have PDK support. So far we’ve written games in JavaScript, TypeScript, and Rust. And we have people attempting games in Zig and Go.

If you want to see GameBox in action, we played a live game of Trivia with the entire audience at Wasm/IO in Barcelona this year. See the first few minutes of Steve’s talk here:

How it works

The primary technical concept behind GameBox is, as lifted from the previous post, about replacing a key GenServer implementation with an Extism plug-in. Like its inspiration, GameBox is built with the Phoenix framework and the GenServer we targeted to extend is LiveView. The LiveView module has a pretty simple API:

It's important to learn about LiveView to understand how the system works and how a game can be created.

LiveView LifeCycle

mount/3 is hand coded for the most part, but handle_info/3 and render/1 are proxied to the game plug-in to handle. You can think of the game as a single state machine. Events come in through handle_info and may or may not mutate the state. When the system needs to be redrawn, render is called and the game outputs some HTML.

Events are defined and triggered on the client using Phoenix bindings. The game programmer doesn’t need to code up a javascript front end, they just need to use the HTML bindings provided by Phoenix. For example, in tic tac toe, a button looks like this:

<button
phx-click="cell-clicked"
phx-value-cell="3"
class="cell">
</button>

When the user clicks this button, an event is sent to the handle_info callback of this shape:


{
"player_id": "benjamin",
"event_name": "cell-clicked",
"value": {
"cell": "3"
}
}

The game logic can interpret this message and mutate the state of the board by placing the player’s character (X or O) at cell index 3.

Game Logic

As mentioned before, the game developer creates a game by satisfying these callbacks. We provide an interface, a number of functions, that the game must export to plug-in to the GameBox system. Thanks to Extism, this interface can be implemented in any language we support.

Jump over to Part III of this series to get the details on how to make a game as a plug-in.

Credits

We got a lot of help from the community on this project and we want to thank all the individuals who put so much work into it:

  • Brian Berlin When we came up with the idea, We were a bit overwhelmed jumping back into Phoenix, but luckily Brian's expertise with Elixir got us far. After a week of hacking we had a solid protoype.
  • Revelry When we decided we wanted to step this up and make this a professional app, we turned to Revelry for help. In a few short weeks they were able to polish it into something we can be proud of.
  • Amy Murphy We also want to callout Amy on the Revelry team who went above and beyond making what are the coolest games on the platform.