go-man api
DESCRIPTION
Learning go-lang and writing a multi player API based gameTRANSCRIPT
go-man APILearning go-lang & writing a game
Why?
By day: I am architect/developer hacking
at Java based api’s
By night: Still harboring a passion for
video games and regularly write small
games to try out new tech on my daily
commute (rarely finish them…)
Fusion
Why not combine both pursuits to retain a
level of interest?
Goals:-
Better understanding of go-lang
Maybe and actual finished skunkworks
project?
Dissecting the game
Broke the idea of “pac-man” down to a
series of API calls.
Create game
Add player(s)
Move players
Repeat to fade
Concurrency
Lightbulb moment!
Multiple players in same game =
concurrency
Parallelism
Multiple games in single server =
parallelism
Caveats
Probably not the best way to do this
First golang code I’ve developed
Highly inefficient
Websockets would’ve been MUCH better
BUT, it’s fun to try it
Starting the game
HTTP POST request to create a new game with following parms:-
Game name
Max number of GoMen
Max number of GoGhosts
Time to wait for players
What happens?
Creates new game on server
Creates golang process waiting for timeout to start game.
Creates a single channel to listen for player updates
Add players to game
HTTP PUT request to add a player to a
game with parms:-
Game id
Player name
Player type (GoMan/GoGhost)
If total players reached game will begin
otherwise game waits for players
Time up
If wait time expires, game server adds remaining players as CPU controlled players.
Creates new golang process for each missing player
Dumb AI:
Wait for ¼ second.
Pick random number between 0-3
Send move request to game channel
repeat…
Game States
“new” – just created
“waiting” – waiting for players to join
“playing” – game active
“over” – all GoMan players have died
“won” – all pills eaten
Power Pills
If a player eats a power pill a separate
golang process is submitted that waits for
duration of pill. At end of timer game reverts
to normal.
Game Client
Originally I started developing a separate
client in go-lang.
Stopped development of this because I
wanted people to try the game easily on the
web.
Switched to JavaScript based client as this
was the most likely client to consume the
API.
Game Client - technology
HTML5 + JavaScript
Twitter bootstrap
HTML5 canvas
Ajax calls to RESTful API
Game Client - features
List games
Create new game
Join existing game
Play game
Game Server
Server implements MVC pattern:
Models = game board + players
View = JSON representation of game state
Controller = RESTful API calls to perform
CRUD on a game instance.
Game Server - technology
Standalone golang executable or
Google app engine app
Uses:
Gorillamux (for http routing)
Game state stored in memory
Game Server – lessons learned
Things I learned during development:-
Started using filesystem as simple datastore for games. Got tired of serialisation / deserialisation and realised it was unnecessary for my simple demo.
Originally modeled board cells as a 2d byte array. But these force client to have to deal with Base64 encoded binary data (nasty). Changed definition to a 2d array of “runes” for an easier life.
BoardCells [][]rune
Game Server – logic
All game logic resides on server. Game state / player state etc.
Game clients are completely dumb.
Weakness in API is that players are not authenticated so they could easily fake each others moves by using the other player’s GUID.
Game Server – concurrency
Game controller writes PlayerMoverequests to a single channel (one per game)
This prevents concurrent updates to same board (two players eaten same pill etc.)
PlayerMove request includes a unique response channel, to return response back to original requester
Game Server – concurrent requests
move me
Game
request
channel
Players can be either remote web players
or local CPU controlled players on server
Game Server – concurrent responses
Players receive a new instance of the
Game board after their move
Bad move
Game Server – stats
golang is FAST!
Normal game 1 goman & 4 goghosts runs in browser @ 60fps
5 x 60 (300) requests per second. Average 3k per response. 1MB per sec traffic.
Running locally it is handled with ease 80+ players in a single game
DO NOT PLAY ON MOBILE PHONE WITH DATA CONTRACT!!!
Game Models - GameBoardtype GameBoard struct {
Id string
Name string
PillsRemaining int
Players map[string]*Player
MaxGoMenAllowed int
MaxGoGhostsAllowed int
WaitForPlayersSeconds int
State GameState
PowerPillsActive int
CreatedTime time.Time
LastUpdatedTime time.Time
GameStartTime time.Time
BoardCells [][]rune
}
Game Models - Player
type Player struct {
Location Point
Id string
Type PlayerType
State PlayerState
Name string
cpuControlled bool
Score int
Lives int
}
Game Models - GameState
const (
NewGame GameState = "new"
WaitingForPlayers = "waiting"
PlayingGame = "playing"
GameWon = "won"
GameOver = "over"
)
Game Models - PlayerState
const (
Alive PlayerState = "alive"
Dying = "dying"
Dead = "dead"
Spawning = "spawning"
)
Game Models - PlayerMove
Request interfacetype PlayerMove struct {
GameId string
PlayerToMove Player
ResponseChannel chan (PlayerMoveResponse)
}
Response interfacetype PlayerMoveResponse struct {
Board GameBoard
Error error
}
Response written back to channel passed in request
ASCII Client
HTML5 Canvas Client
Demo
Client: http://go-man-client.herokuapp.com/
(Hosted as simple static site)
Source: https://github.com/telecoda/go-man-javascript-client
API: http://go-man-app.appspot.com
(Hosted on single GAE instance)
Source: https://github.com/telecoda/go-man-app
Wiki: https://github.com/telecoda/go-man-app/wiki