server/protocol.go
Stephen McQuay 6fd4138740 added bw calcs
unfortunately there is an issue with how these are calculated, and they don't
decrease as things quiet down. Well, the bad numbers are their for your
consumption, and should Just Work TM when appropriate changes in
bitbucket.org/smcquay/bandwith land. And they will. But I'm tired now.
2014-03-03 22:56:26 -08:00

309 lines
7.2 KiB
Go

package main
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, "usergent 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"`
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 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 {
var err error
game, err = NewGame(
gid.Id,
float32(conf.Width),
float32(conf.Height),
conf.Obstacles,
conf.Tick,
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()
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
} 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
}
gameParam := game.gameParam()
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
} 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)
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: idg.Hash(),
Name: name,
Health: 10,
Heading: v.Vector2d{1, 0},
Scanners: make([]Scanner, 0)}
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)
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)
}
}