package main import ( v "bitbucket.org/hackerbots/vector" "code.google.com/p/go.net/websocket" "log" ) // < the name of the game we want to join type GameID struct { Id string `json:"id"` } // > identify type PlayerID struct { Type string `json:"type"` Hash string `json:"id"` Failure } func NewPlayerID(id string) *PlayerID { return &PlayerID{ Type: "idreq", Hash: id, } } // < [robot | spectator], name, client-type, game ID type ClientID struct { Type string `json:"type"` Name string `json:"name"` Useragent string `json:"useragent"` } func (c *ClientID) Valid() (bool, string) { switch c.Type { case "robot", "spectator": return true, "" } return false, "usergent must be 'robot' or 'spectator'" } type ClientConfig struct { ID string `json:"id"` Stats map[string]StatsRequest `json:"stats"` } func (config ClientConfig) Valid() bool { total := 0 for _, s := range config.Stats { total += (s.Speed + s.Hp + s.WeaponRadius + s.ScannerRadius + s.Acceleration + s.TurnSpeed + s.FireRate + s.WeaponDamage + s.WeaponSpeed) } // allowing for 50 pts in every category if total > 500 { return false } return true } type BoardSize struct { Width float32 `json:"width"` Height float32 `json:"height"` } type GameParam struct { BoardSize BoardSize `json:"boardsize"` Type string `json:"type"` } // > [OK | FULL | NOT AUTH], board size, game params func NewGameParam(w, h float32) *GameParam { return &GameParam{ BoardSize: BoardSize{ Width: w, Height: h, }, Type: "gameparam", } } type Handshake struct { ID string `json:"id"` Success bool `json:"success"` Type string `json:"type"` } func NewHandshake(id string, success bool) *Handshake { return &Handshake{ ID: id, Success: success, Type: "handshake", } } type Message interface { } type Boardstate struct { MyRobots []Robot `json:"my_robots"` OtherRobots []OtherRobot `json:"robots"` Projectiles []Projectile `json:"projectiles"` Splosions []Splosion `json:"splosions"` Obstacles []Obstacle `json:"obj"` Type string `json:"type"` Turn int `json:"turn"` AllBots []BotHealth `json:"all_bots"` Messages []string `json:"messages"` } func NewBoardstate() *Boardstate { return &Boardstate{ MyRobots: []Robot{}, OtherRobots: []OtherRobot{}, Projectiles: []Projectile{}, Splosions: []Splosion{}, AllBots: []BotHealth{}, Type: "boardstate", } } type GameOver struct { Winners []string `json:"winners"` Type string `json:"type"` } func NewGameOver() *GameOver { return &GameOver{ Type: "gameover", Winners: make([]string, 0), } } type Failure struct { Reason string `json:"reason"` Type string `json:"type"` } func NewFailure(reason string) *Failure { return &Failure{ Reason: reason, Type: "failure", } } func addPlayer(ws *websocket.Conn) { var gid GameID err := websocket.JSON.Receive(ws, &gid) if err != nil { log.Println("problem parsing the requested game id") return } game := games.get(gid.Id) if game == nil { game = NewGame( gid.Id, float32(conf.Width), float32(conf.Height), conf.Tick, ) go game.run() games.add(game) } player_id := idg.Hash() err = websocket.JSON.Send(ws, NewPlayerID(player_id)) if err != nil { log.Printf("game %s: unable to send player_id to player %s", gid.Id, player_id) websocket.JSON.Send(ws, NewFailure("send error")) return } var clientid ClientID err = websocket.JSON.Receive(ws, &clientid) if err != nil { log.Printf("unable to parse ClientID: gid: %s, player: %s", gid.Id, player_id) websocket.JSON.Send(ws, NewFailure("parse error")) return } if v, msg := clientid.Valid(); !v { log.Printf("clientid is invalid: %+v", clientid) websocket.JSON.Send( ws, NewFailure(msg), ) return } gameParam := NewGameParam(game.width, game.height) err = websocket.JSON.Send(ws, gameParam) if err != nil { log.Printf("%s %s game param parse error", gid.Id, player_id) websocket.JSON.Send(ws, NewFailure("game param parse error")) return } switch clientid.Type { case "robot": var conf ClientConfig for { log.Printf("%s Waiting for client to send conf ...", player_id) err = websocket.JSON.Receive(ws, &conf) log.Printf("conf received: %+v", conf) if err != nil { log.Printf("%s %s config parse error", gid.Id, player_id) websocket.JSON.Send(ws, NewFailure("config parse error")) return } // TODO: verify conf's type if conf.Valid() { log.Printf("Config is Valid, continuing") _ = websocket.JSON.Send(ws, NewHandshake(player_id, true)) break } else { log.Printf("Config is INVALID, abort") _ = websocket.JSON.Send(ws, NewHandshake(player_id, false)) } } p := &player{ Robots: []*Robot{}, send: make(chan Message), ws: ws, } for name, stats := range conf.Stats { r := Robot{ Stats: DeriveStats(stats), Id: idg.Hash(), Name: name, Health: 10, Heading: v.Vector2d{1, 0}, Scanners: make([]Scanner, 0)} r.Health = r.Stats.Hp log.Printf("Adding Robot: %+v", r) r.reset(game) p.Robots = append(p.Robots, &r) } game.register <- p defer func() { game.unregister <- p }() go p.sender() p.recv() log.Printf("game %s: player %v has been disconnected from this game\n", gid.Id, p.Robots[0].Id) case "spectator": s := &Spectator{ send: make(chan Message), ws: ws, } game.sregister <- s defer func() { game.sunregister <- s }() s.sender() log.Printf("game %s: spectator %+v has been disconnected from this game\n", s) } }