diff --git a/control.go b/control.go index 316e7bd..2557650 100644 --- a/control.go +++ b/control.go @@ -57,7 +57,13 @@ func startGame(w http.ResponseWriter, req *http.Request) { g := games.get(requested_game_name) if g == nil { log.Printf("Game '%s' non-existant; making it now", requested_game_name) - g = NewGame(requested_game_name, width, height, obstacles, conf.Tick, maxPoints, mode) + g, err := NewGame(requested_game_name, width, height, obstacles, conf.Tick, maxPoints, mode) + if err != nil { + log.Printf("problem creating game: %s: %s", requested_game_name, err) + b, _ := json.Marshal(NewFailure("game creation failure")) + http.Error(w, string(b), http.StatusConflict) + return + } go g.run() games.add(g) } else { @@ -136,6 +142,33 @@ func gameStats(w http.ResponseWriter, req *http.Request) { } } +func bw(w http.ResponseWriter, req *http.Request) { + // TODO: wrap this up in something similar to the JsonHandler to verify the + // url? Look at gorilla routing? + key, err := getGameId(req.URL.Path) + if err != nil { + b, _ := json.Marshal(NewFailure(err.Error())) + http.Error(w, string(b), http.StatusBadRequest) + return + } + log.Printf("requested bandwidth for game: %s", key) + games.RLock() + g, ok := games.m[key] + games.RUnlock() + if !ok { + b, _ := json.Marshal(NewFailure("game not found")) + http.Error(w, string(b), http.StatusNotFound) + return + } + s := map[string][]float64{ + "tx": <-g.bw.Tx, + "rx": <-g.bw.Rx, + } + if err := json.NewEncoder(w).Encode(s); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + func stopGame(w http.ResponseWriter, req *http.Request) { key, err := getGameId(req.URL.Path) if err != nil { diff --git a/game.go b/game.go index 3457914..96d040d 100644 --- a/game.go +++ b/game.go @@ -7,6 +7,8 @@ import ( "sort" "sync" "time" + + "bitbucket.org/smcquay/bandwidth" ) const maxPlayer = 128 @@ -84,6 +86,7 @@ type game struct { tick_duration int stats GameStats mode GameMode + bw *bandwidth.Bandwidth } type GameMode interface { @@ -92,7 +95,16 @@ type GameMode interface { gameOver(gg *game) (bool, *GameOver) } -func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) *game { +func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, mode string) (*game, error) { + bw, err := bandwidth.NewBandwidth( + []int{1, 10, 60}, + time.Duration(500)*time.Millisecond, + ) + if err != nil { + log.Fatal("seriously, what the fuck") + return nil, err + } + go bw.Run() g := &game{ id: id, register: make(chan *player, maxPlayer), @@ -115,6 +127,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m tick_duration: tick, players_remaining: 2, stats: GameStats{PlayerStats: make(map[string]*PlayerStats)}, + bw: bw, } if mode == "melee" { @@ -125,7 +138,7 @@ func NewGame(id string, width, height float32, obstacles, tick, maxPoints int, m g.mode.setup(g) - return g + return g, nil } func (g *game) tick(payload *Boardstate) { @@ -286,6 +299,7 @@ func (g *game) run() { select { case <-g.kill: log.Printf("game %s: received kill signal, dying gracefully", g.id) + close(g.bw.Quit) games.Lock() for player := range g.players { close(player.send) diff --git a/main.go b/main.go index 1a5ce9c..ac2b931 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,7 @@ func main() { sm.Handle("/game/start/", JsonHandler(startGame)) sm.Handle("/game/list/", JsonHandler(listGames)) sm.Handle("/game/stats/", JsonHandler(gameStats)) + sm.Handle("/game/bw/", JsonHandler(bw)) sm.Handle("/game/stop/", JsonHandler(stopGame)) sm.HandleFunc("/fuck/shit/up/", killServer) diff --git a/player.go b/player.go index 13f86de..ac6e211 100644 --- a/player.go +++ b/player.go @@ -1,26 +1,30 @@ package main import ( - "code.google.com/p/go.net/websocket" "encoding/json" "errors" "fmt" "log" + + "bitbucket.org/smcquay/bandwidth" + "code.google.com/p/go.net/websocket" ) const maxMessageSize = 1024 type protoTalker struct { ws *websocket.Conn + bw *bandwidth.Bandwidth send chan Message buff []byte Id string } -func NewProtoTalker(id string, ws *websocket.Conn) *protoTalker { +func NewProtoTalker(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth) *protoTalker { return &protoTalker{ send: make(chan Message, 16), ws: ws, + bw: bw, Id: id, buff: make([]byte, maxMessageSize), } @@ -33,8 +37,8 @@ func (pt *protoTalker) sender() { if err != nil { break } - // XXX: send the count to our bandwidth analyzer ... - _, err = pt.ws.Write(b) + n, err := pt.ws.Write(b) + pt.bw.AddTx <- n if err != nil { break } @@ -50,7 +54,7 @@ func (pt *protoTalker) readJSON(buff []byte) (map[string]Instruction, error) { log.Printf("%s: problem reading from player: %s", pt.Id, err) return nil, err } - // XXX: send n to our bandwidth analyzer ... + pt.bw.AddRx <- n if n == len(buff) { errMsg := fmt.Sprintf("%s: read buffer overfull: %s", pt.Id, string(buff)) log.Printf(errMsg) @@ -70,10 +74,10 @@ type player struct { protoTalker } -func NewPlayer(id string, ws *websocket.Conn) *player { +func NewPlayer(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth) *player { return &player{ Robots: []*Robot{}, - protoTalker: *NewProtoTalker(id, ws), + protoTalker: *NewProtoTalker(id, ws, bw), } } @@ -152,9 +156,9 @@ type Spectator struct { protoTalker } -func NewSpectator(id string, ws *websocket.Conn) *Spectator { +func NewSpectator(id string, ws *websocket.Conn, bw *bandwidth.Bandwidth) *Spectator { return &Spectator{ - protoTalker: *NewProtoTalker(id, ws), + protoTalker: *NewProtoTalker(id, ws, bw), } } diff --git a/protocol.go b/protocol.go index 1124145..4ef49e9 100644 --- a/protocol.go +++ b/protocol.go @@ -153,7 +153,8 @@ func addPlayer(ws *websocket.Conn) { game := games.get(gid.Id) if game == nil { - game = NewGame( + var err error + game, err = NewGame( gid.Id, float32(conf.Width), float32(conf.Height), @@ -162,6 +163,11 @@ func addPlayer(ws *websocket.Conn) { 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) } @@ -230,7 +236,7 @@ func addPlayer(ws *websocket.Conn) { } } - p := NewPlayer(player_id, ws) + p := NewPlayer(player_id, ws, game.bw) log.Printf("%s: made a player: %s", gid.Id, p.Id) convertedStats := map[string]Stats{} @@ -285,7 +291,7 @@ func addPlayer(ws *websocket.Conn) { gid.Id, ) case "spectator": - s := NewSpectator(player_id, ws) + 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)