Showing posts with label game. Show all posts
Showing posts with label game. Show all posts

Thursday, December 22, 2011

Tic-Tac-Toe with Redis and Backbone.js

This post is a technical overview of my Tic-Tac-Toe implementation. It is a zero server-side logic, pub-sub based, realtime, multiplayer game which uses Redis and Backbone as the key enablers of real-time and moving logic to the client side, respectively. Now that all the buzz-words are out of the way, the source code is on Github. There are rough edges with reliable communication, security holes do exist, but for the most part it works well.
I would specially like to thank Nicolas Favre-Félix for Webdis without which this would’ve been impossible.

Basic functioning

On visiting the home page the user has an option to either join a game, or play with a friend. The join game option pairs the player with another player who also wants to play (if you are the only one online, you’ll have to wait). If you play with a friend you get a link you can send him/her so you can play together.
The players are asymmetric in the sense that there is a ‘hoster’ and a ‘joiner’, whose roles I’ll get into in a bit.

Technology Stack

Tic-Tac-Toe game me the opportunity to experiment with a ton of technologies/products I hadn’t handled before. The stack goes like this.
  • A Linode VM hosts the service
  • Redis powers base Pub/Sub
  • Webdis provides a REST API to Redis so that the clients can directly talk to it
  • nginx serves static files
  • haproxy routes requests to nginx or Webdis depending on the path
  • Backbone is used for MVC
  • Raphaël is used to draw the grid and pieces using SVG
  • jQuery is used for AJAX and the impromptu is used for modal dialogs
  • underscore is a utility belt
  • UUID.js for generating UUIDs
Yep, thats a lot of stuff for something so simple, so I’ve tried to rationalize it below :P

Clients

All clients are identified by a UUID. The ‘host’ player’s UUID is used as the name of the Redis Pub/Sub channel. The ‘joiner’ will also subscribe to this channel and publish to it. This is why the ‘host’, ‘join’ bifurcation, so that a common channel can be used for communication. All messages are JSON objects.
All communication is initiated by the ‘joiner’. The host player keeps waiting. The joiner starts the game, after which both players keep sending each other the moves made by the humans playing. After each move, both sides check for a win/lose/draw. Again, the joiner sends a gameover message when it detects somebody has won (or it’s a draw). The host then verifies that this is actually true, and responds back with it’s own gameover message. Then both parties change their UI accordingly. This causes the slight delay between the actual win and the notification.
WSD for Tic-Tac-Toe

Server side

The first question is ‘why both nginx and haproxy?’ I could’ve run Webdis and nginx on two different ports since Webdis supports CORS, but Opera does not support it. Running just nginx web-facing and then proxying to Webdis based on path is also not possible since nginx does not currently support HTTP chunked replies, while haproxy does. Chunked replies are used by Webdis to relay Pub/Sub messages.

Implementing ‘Join Game’

To allow two people to be paired, a Redis list is maintained. When a client clicks Join Game it attempts to LPOP a UUID off the list. If none are found (no other players), it RPUSHes itself onto the list.

Client side

The client side has all the logic and so is more interesting. I won’t go deep (you can read the source), but will discuss the key areas.
Once a game has been initiated, both clients open a XMLHttpRequest to the subscriber channel. They watch for readystatechange events and try to parse the messages. This is the Subscriber which fires events when it receives valid messages.
The Publisher component is used to send messages, while Webdis is used for other Redis commands (currently only LPOP and RPUSH).
The GameRouter is the controller, sets up models and views, watches for gameover and beginning a host or join. HTML5 pushState or hashes are used to offer permalinks for games. /host/UUID is for hosting and /play/UUID is the joiner. Backbone’s Router and History is used to cleanly handle this. One feature I wish there was, is a way to query what the current route is via Backbone instead of mucking with window.location.
The GridModel drives the game once it begins, triggering gameover events and checking the grid after every move, also handling the turns. GridView handles rendering the SVG grid and pieces based on GridModel and processes clicks while HUDView notifies the user of his turn, piece and game state. Events are an integral part of this entire setup and Backbone makes it very simple and modularized.
One thing that stands out in my use of Backbone is the absence of Sync. I’ve not used it, although it integrates well with Backbone. The game model didn’t seem suited to it, being more event-based rather than the model reflecting the game state. Collection is also not used since there is only one grid.

Issues

The lack of any server side code does lead to certain issues. None of them are serious when it comes to Tic-Tac-Toe but some affect the user experience and others are security issues.

Usability

On the usability front, the current code is very fickle. The joiner only tries to initiate the connection the first time it starts. If the host fails to respond for some reason (e.g. network connectivity) then both parties will keep waiting. If a joiner refreshes his page mid-way through a game, the game will restart for both parties (a good idea if you are a joiner and you are losing \:P). If the host refreshes the page, the joiner has to refresh after him.
The slight lag to decide win/lose/draw was probably unnecessary. Rather than verifying both sides are on the same page, the notification could’ve been shown directly since this is just a game. The UX would’ve benefited.

Security

Webdis sports a HTTP interface to Redis, but there is no real authentication support, which means the Redis instance hosting Tic-Tac-Toe is also effectively a public domain database (although the command set is restricted). Ideally this should run only in a trusted intranet. Ideally Webdis itself would be capable of serving files and deliver something like a nonce which it would then use to ensure that only its own connections are allowed to relay messages. Similarly nothing is stopping someone from grabbing the UUID and then playing future moves using a bot of some kind to always play optimally. In the case of Tic-Tac-Toe this is just a minor prick, but for actual client-side MVC applications this requires fixing.

Conclusion

Tic-Tac-Toe demonstrates that with a data structure server and HTTP interface to it, web applications where all logic is client-side are possible. Using the server as a Pub/Sub relay also allows near real-time performance, with WebSockets or SPDY possibly leading to better performance. Security policies still mean that true peer-to-peer isn’t possible. Authenticity and authorization also remain to be solved. On trusted intranets, such applications could be used for non-critical tasks. Meanwhile, games like this at least can be safely implemented and played as long as you have no sore losers :).

Sunday, May 17, 2009

May updates

I'm home! Till almost the end of July I'll be @ home. Which means a better internet connection at the very least :p

So I've been swimming and learning to drive! Driving is fun, I've finished five days out of the 22 and I've managed not to hit anyone or anything.

In programming, I'm working on a game using Canvas for the CodeChef game contest. You can track its progress on github.

Also I've been doing some C socket programming, mainly doing segmented multithreaded HTTP downloads. Right now all the code is prototype, but someday it will be a decent download manager.

I'm also working on implementing tiling in KWin along with Martin Graesslin, my mentor. Right now there isn't much real progress apart from a KWin branch which maximizes every window you open.

And yesterday I got a copy of Real World Haskell. Yay!

Saturday, December 08, 2007

Introducing Ricochet

I've finally finished the game I was working on.

World, meet Ricochet.

Ricochet is a tile based game in which you use various kinds of mirrors to guide a photon from the launcher and switch on all the switches.
After a lot of segfaults and architecture changes due to my intermediate knowledge of C++, it finally seems to be bugfree.
The only thing left to do is add a help, add more levels and make a few small changes, after which it will be released. For now enjoy the screenshots.

Menu:



Level with blocks still in dock:



Energised photon:

Monday, October 09, 2006

LaserMania development


Lasermania finally has something to see moving. Its this game where you have to arrange specific mirrors in proper places to guide a photon from emitter to reciever. Its written in pygame/python. In the screenshot you can see the photon(red dot) to the right. The emitter now fires a photon, drag and drop works(almost), only the mirror logic is left.