- package botserv
-
- import (
- "log"
-
- v "bitbucket.org/hackerbots/vector"
- "code.google.com/p/go.net/websocket"
- )
-
- // < 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, "useragent must be 'robot' or 'spectator'"
- }
-
- type ClientConfig struct {
- ID string `json:"id"`
- Stats map[string]StatsRequest `json:"stats"`
- }
-
- func (config ClientConfig) Valid(max int) 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)
- }
- if total > max {
- return false
- }
- return true
- }
-
- type BoardSize struct {
- Width float32 `json:"width"`
- Height float32 `json:"height"`
- }
-
- type GameParam struct {
- // TODO: should have information about max points in here
- BoardSize BoardSize `json:"boardsize"`
- MaxPoints int `json:"max_points"`
- Encoding string `json:"encoding"`
- Type string `json:"type"`
- }
-
- // > [OK | FULL | NOT AUTH], board size, game params
-
- 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"`
- Objects [][4]int `json:"objects"`
- 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 (c *Controller) 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 := c.Games.Get(gid.Id)
- if game == nil {
- var err error
- game, err = NewGame(
- gid.Id,
- float32(c.Conf.Width),
- float32(c.Conf.Height),
- c.Conf.Obstacles,
- c.Conf.Tick,
- c.Conf.MaxPoints,
- "",
- )
- if err != nil {
- log.Printf("problem creating game: %s", gid.Id)
- websocket.JSON.Send(ws, NewFailure("game creation error"))
- return
- }
- go game.run()
- c.Games.Add(game)
- }
-
- player_id := c.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
- } else {
- log.Printf("game %s: sent player id: %s", gid.Id, player_id)
- }
-
- 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
- } else {
- log.Printf("game %s: recieved: %+v", gid.Id, clientid)
- }
- if v, msg := clientid.Valid(); !v {
- log.Printf("clientid is invalid: %+v", clientid)
- websocket.JSON.Send(
- ws,
- NewFailure(msg),
- )
- return
- }
-
- reqEncs := []string{}
- err = websocket.JSON.Receive(ws, &reqEncs)
- if err != nil {
- log.Printf("%s %s unable to parse requested encodings", gid.Id, player_id)
- websocket.JSON.Send(ws, NewFailure("encoding recieve error"))
- return
- }
- prefEncs := []string{
- "gob",
- "json",
- }
-
- var encoding string
- encodingLoops:
- for _, prefEnc := range prefEncs {
- for _, reqEnc := range reqEncs {
- if reqEnc == prefEnc {
- encoding = prefEnc
- log.Println("selected following encoding:", encoding)
- break encodingLoops
- }
- }
- }
- if encoding == "" {
- log.Printf("%s %s unable to negociate encoding", gid.Id, player_id)
- websocket.JSON.Send(
- ws,
- NewFailure("no overlap on supported encodings; I suggest using json"),
- )
- return
- }
-
- gameParam := game.gameParam()
- gp := struct {
- GameParam
- Encoding string `json:"encoding"`
- }{
- GameParam: *gameParam,
- Encoding: encoding,
- }
- err = websocket.JSON.Send(ws, gp)
- if err != nil {
- log.Printf("%s %s game param send error", gid.Id, player_id)
- websocket.JSON.Send(ws, NewFailure("game param send error"))
- return
- } else {
- log.Printf("%s -> %s: sent %+v", gid.Id, player_id, gameParam)
- }
-
- 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("%s: conf received: %s", player_id, conf.ID)
-
- 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(game.maxPoints) {
- log.Printf("%s -> %s: valid client config", gid.Id, player_id)
- _ = websocket.JSON.Send(ws, NewHandshake(player_id, true))
- break
- } else {
- log.Printf("%s: Config is INVALID, abort", player_id)
- _ = websocket.JSON.Send(ws, NewFailure("invalid config"))
- return
- }
- }
-
- p := NewPlayer(player_id, ws, game.bw, encoding)
- log.Printf("%s: made a player: %s", gid.Id, p.Id)
-
- convertedStats := map[string]Stats{}
- for name, stats := range conf.Stats {
- dstat := DeriveStats(stats)
- convertedStats[name] = dstat
- r := Robot{
- Stats: dstat,
- Id: c.Idg.Hash(),
- Name: name,
- Health: 10,
- Heading: v.Vector2d{1, 0},
- Scanners: make([]Scanner, 0),
- Delta: c.Conf.Delta,
- idg: c.Idg,
- }
- r.Health = r.Stats.Hp
- log.Printf("%s: adding robot: %s", p.Id, r.Id)
- r.reset(game)
- p.Robots = append(p.Robots, &r)
- }
-
- statsPayload := struct {
- Stats map[string]Stats `json:"stats"`
- Type string `json:"type"`
- }{
- Stats: convertedStats,
- Type: "stats",
- }
- err = websocket.JSON.Send(ws, &statsPayload)
- if err != nil {
- log.Printf("error sending convertedStats to client: %s", err)
- websocket.JSON.Send(ws, NewFailure("protocol error: convertedStats"))
- return
- } else {
- log.Printf("%s -> %s: sent stats payload", gid.Id, p.Id)
- }
-
- log.Printf("%s, %s: about to register this player", gid.Id, p.Id)
- game.register <- p
- log.Printf("%s, %s: registered player", gid.Id, p.Id)
-
- defer func() {
- log.Printf("%s, %s: about to unregister this player", gid.Id, p.Id)
- game.unregister <- p
- log.Printf("%s, %s: unregistered player", gid.Id, p.Id)
- }()
- go p.sender()
- log.Printf("%s -> %s: p.sender went", gid.Id, p.Id)
- p.recv()
- log.Printf(
- "%s (player): %v (robot) has been disconnected from %s (game)",
- p.Id,
- p.Robots[0].Id,
- gid.Id,
- )
- case "spectator":
- s := NewSpectator(player_id, ws, game.bw, encoding)
- log.Printf("%s, %s: about to register this spectator", gid.Id, s.Id)
- game.sregister <- s
- log.Printf("%s, %s: registered spectator", gid.Id, s.Id)
- defer func() {
- log.Printf("%s, %s: about to unregister this spectator", gid.Id, s.Id)
- game.sunregister <- s
- log.Printf("%s, %s: unregistered spectator", gid.Id, s.Id)
- }()
- go s.sender()
- log.Printf("%s -> %s: s.sender went", gid.Id, s.Id)
- s.recv()
- log.Printf("game %s: spectator %+v has been disconnected from this game", gid.Id, s)
- }
- log.Printf("exiting AddPlayer")
- }
|