Evolving an API for multiplayer games

Peter Szerzo
Jul 22, 2017 · 8 min read

How easily can we code up reasonably custom multiplayer games? I’ve been poking at this question for just about a year now, anticipating a world where if I had to pick up a skill, teach my nephew physics or came up with a silly new idea to annoy my friends after a dinner party, I could write up a game for it in a few hundred lines, only thinking about what is unique to the new experience and leaving the rest to a reliable abstraction. Not having to worry about scorekeeping logic, timing logic, or any kind of custom back-end work every time sounded pretty great.

Lettero is the original game that lead my thinking about a multiplayer game abstraction. In it, players compete in finding the first letter of a word written in a circle. This image just happens to spell out ‘easily’.

To take a stab at the problem, I sketched out a package called elm-gameroom, which I was so very fortunate to present at Elm Europe this year. You don’t need to watch the video to understand the rest of this post, but if you’re up for getting some context delivered in the form of refreshingly awkward public speaking, then you should definitely go for it 🤓.

Having a great time talking about the initial version of elm-gameroom, in the company of many wonderful people. In an uncomplicated suburb of Paris, June 2017.

With so many brilliant ideas floating around at the conference, I was inspired to come up with a clearer, simpler-to-use and whole lot more customizable elm-gameroom@3.0, which I’m announcing today 🎉.

Here’s what’s been going on:

Learnability, fun and focus

Every time I remind myself that APIs cannot possibly be simpler than the idea they’re implementing, I feel heartbroken. A multiplayer game needs views, event handlers, game logic, timers, and most finicky of all, a way to sync state between machines. All of this needs to be in place before a game can be played — but can we show something on the screen a few steps earlier to not overwhelm newcomers?

I haven’t thought about this at all before the conference, so elm-gameroom@2.0 simply required everything to be set up before Main.elm even compiled. And even though it was just following a set of instructions and copying some boilerplate, it was still a lot. Would I greet my dinner guests by telling them straight away that once they cleaned their shoes and washed their hands in the upstairs bathroom, they could go ahead and cook their food in the kitchen? Even if every one of these steps seem super simple to me?

Charmingly rude, I thought, but why don’t I just go ahead and do a little better. In 3.0, it is now enough to specify the bare essentials — view and game logic — and not think at all about any back-end to see a single-player simulation (or tutorial) on the screen. Getting started is more fun, with fewer moving parts to think about — for instance, a country capital quiz game called The Capitalist can be made in 120 lines, following these steps:

  • Define the data structure for the game problem, in this case a record that includes a question, a list of answers, and the index of the correct one. Supply some encoders and decoders:
  • Define the data structure for the guess — in this case, it is the index of the selected answer. Again, supply those encoders and decoders:
  • Define a way to generate problems. Use a helper supplied by the framework:
  • Evaluate a guess corresponding to a problem (highest evaluation wins a round):
  • Define how a problem should look on the screen, mapping any user input to raw guesses (in this case, onClick maps to simple integers from zero to number of answers minus one). The context variable includes things like the state of the round (active, cooldown, etc.), the players’ guesses (so you can include feedback) and scores. You shouldn’t worry about this starting out, and you can see it documented in its own module Gameroom.Context.
  • Mash all things together into a record called spec. (Spec is a type supplied by elm-gameroom, and takes type variables for Problem and Guess):
  • Your game is now ready:

Notice that you don’t have to supply init, view, update and subscriptions, and Model and Msg are types supplied by the framework for the annotations. The specific data structure for problems and guesses shows up everywhere, since they’re passed around as type variables all over the place in the framework’s codebase.

elm-gameroom takes care that at this stage, the UI tells the game dev that multiplayer routes — create a room, play with others — are functional yet, since they rely on outside communication working correctly.

No working multiplayer warning for routes other than tutorial.

Now that the bulk is set up and we see how things behave, setting up multiplayer is just another step:

  • Define some ports taking JavaScript objects:
  • Supply these ports to a modified gameWith function, keeping the spec object the same as before:

elm-gameroom games need to talk to some back-end, and since I wanted to keep this back-end generic, there have to be some ports. Why responsiblePorts? Because communication with these ports has to be set up correctly with the back-end of your choosing, and even though this pretty much means copying some boilerplate, it can still go wrong. A more conventional name would be unsafePorts, but I thought responsible sounded a little more trustful and encouraging.

So many more customizations

I gave away the customization API just above with gameWith and the very first and most important customization option, responsiblePorts. To take a step back, this customization strategy now follows the Settings API from elm-community/webgl, allowing all kinds of optional customizations without bloating the spec object, such as:

  • noPeripheralUi: there is a peripheral interface – including a score board, winner notifications and a timer – during gameplay. Chances are you won’t like it, so you can disable it completely, and build up the peripheral UI on its own. All the data for it is provided to you in Context.
  • clearWinner: it used to be that a guess was either correct or incorrect — indeed, the evaluate function from the spec used to be isGuessCorrect. Now, guesses are evaluated to a float value, the highest of which wins each round. To preserve the way things worked in the old days, you can, for example, set a clearWinner 100 setting to tell the app that only players who reach the maximum evaluation of 100 get to win any given round.
  • roundDuration,cooldownDuration, unicodeIcon, name, instructions, etCetera: can you imagine there was a time these were once hard-coded?

You can mix and match things like so:

Having these settings come in a list allow gradual customization while keeping sensible defaults for all the parameters you’re not touching. And of course, you can see the effect of every step you take on the screen.

Just one-step guessing games?

Not anymore! Any player can now make as many moves as they like while the game round is active (of course, as game author, you can take this freedom away, as it makes sense in a lot of games). A good friend of mine referred me to Simon Tatham’s awesome puzzle collection, and I am happy to report that most of these are portable to the new elm-gameroom. Stay tuned to a more elaborate example showing up in the repository in the coming weeks (ok, maybe months).

Simon Tatham’s Portable Puzzle Collection

Should there be a collaborative mode? Can a new game round build upon the state of the previous one, forming more of a cohesive story? Is there a point where the framework becomes too generic to be easily used, perhaps still not generic enough to allow us to go someplace really cool with it? These are still questions I’m looking into. I’m thinking of adding a complexGame option in the API that mimics the original game API but exposes all kinds of new parameters to the functions required within the game spec. It would be cool to see what new kinds of game experiences it will encourage.

Deployment

I realize that making games easy to code up wouldn’t matter all that much if they were still hard to deploy. I’m on it! I’d like to have a central game site that would pull source codes from GitHub repositories, and allow folks to submit their own games. Making stuff on Ellie sounds even more streamlined, and I’ll be exploring that sometime in the fall.

And saving the best for last: Fast and Moebius!

At Elm Europe, I only showed a dummy prototype for it, but I am thrilled to announce that the unapologetically punny game is now fully functional (as in working, but also like, you know, λ·functional·λ 🤓)! So go bet on your favorite drivers.

Fast and Moebius is ready to play!

So there it is, all the highlights from elm-gameroom@3.0. Some of my teacher/volunteer friends are excited about an educational potential for the project, but other than that, its scope will probably remain wide and casual. I’ll be thrilled to hear your thoughts, and humbled if should decide to take it for a spin yourself 🚲.

Until then, happy gaming, for example on Alto’s Adventure, Limbo or the brilliant Tampon Run.


Thanks Andrey for your valuable API feedback and help with the webgl library.

Peter Szerzo

Written by

Your friendly neighborhood creative programmer.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade