package botclient import ( "encoding/gob" "encoding/json" "errors" "fmt" "log" "bitbucket.org/hackerbots/botserv" "code.google.com/p/go.net/websocket" ) func connect(server string, port int) (*websocket.Conn, error) { origin := "http://localhost/" url := fmt.Sprintf("ws://%s:%d/ws/", server, port) return websocket.Dial(url, "", origin) } type Client struct { ForceJSON bool GameId string Name string Port int Server string StatsReq botserv.StatsRequest Verbose bool Player Player Game botserv.GameParam boardstate botserv.Boardstate ws *websocket.Conn } type encoder interface { Encode(v interface{}) error } type decoder interface { Decode(v interface{}) error } func (c *Client) Negociate() (err error) { if c.Verbose { log.Printf("%s: trying to connect to game '%s'", c.Name, c.GameId) } c.ws, err = connect(c.Server, c.Port) if err != nil { return errors.New(fmt.Sprintf("connection failure: %s", err)) } err = websocket.JSON.Send(c.ws, struct { Id string `json:"id"` }{ c.GameId, }) if err != nil { return err } var idreq struct { Type string `json:"type"` PlayerId string `json:"id"` } err = websocket.JSON.Receive(c.ws, &idreq) if err != nil || idreq.Type == "failure" { return errors.New(fmt.Sprintf("failure: %+v", idreq)) } if c.Verbose { log.Printf("%s: idreq: %+v", c.Name, idreq) } err = websocket.JSON.Send(c.ws, struct { Type string `json:"type"` Name string `json:"name"` Useragent string `json:"useragent"` }{ Name: c.Name, Useragent: "gobot", Type: "robot", }) if err != nil { return err } supportedEncs := []string{"bson", "json", "gob"} if c.ForceJSON { supportedEncs = []string{"json"} } err = websocket.JSON.Send(c.ws, supportedEncs) if err != nil { return errors.New(fmt.Sprintf("failure: %+v", err)) } err = websocket.JSON.Receive(c.ws, &c.Game) if c.Game.Type != "gameparam" { return errors.New("didn't receive a good gameparam") } if c.Verbose { log.Printf("%s: game parameters: %+v", c.Name, c.Game) } conf := botserv.ClientConfig{ ID: c.GameId, Stats: map[string]botserv.StatsRequest{ c.Name: c.StatsReq, }, } err = websocket.JSON.Send(c.ws, conf) var handshake struct { Id string `json:"id"` Success bool `json:"success"` Type string `json:"type"` botserv.Failure } websocket.JSON.Receive(c.ws, &handshake) if !handshake.Success { return errors.New(handshake.Reason) } if c.Verbose { log.Printf("%s: handshake: %+v", c.Name, handshake) } // we don't do anything useful with dstats, but could be interesting to // pass along to the player? dstats := struct { Stats map[string]botserv.Stats `json:"stats"` Type string `json:"type"` }{} err = websocket.JSON.Receive(c.ws, &dstats) if err != nil { return err } return nil } func (c *Client) Play() error { log.Printf("%s: starting loop", c.Name) var enc encoder var dec decoder if c.Game.Encoding == "json" { enc = json.NewEncoder(c.ws) dec = json.NewDecoder(c.ws) } else { enc = gob.NewEncoder(c.ws) dec = gob.NewDecoder(c.ws) } var err error for { err = dec.Decode(&c.boardstate) if err != nil { return errors.New(fmt.Sprintf("%s: Connection likely lost: %s", c.Name, err)) } c.Player.Recv(&c.boardstate) instruction := c.Player.Instruction() err = enc.Encode(instruction) if err != nil { return err } } return nil }