Stephen McQuay
6fd4138740
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.
232 lines
5.7 KiB
Go
232 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"runtime/pprof"
|
|
"strings"
|
|
)
|
|
|
|
type JsonHandler func(http.ResponseWriter, *http.Request)
|
|
|
|
func (h JsonHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
h(w, req)
|
|
}
|
|
|
|
func startGame(w http.ResponseWriter, req *http.Request) {
|
|
log.Println("asked to create a game")
|
|
|
|
requested_game_name := idg.Hash()
|
|
width, height := float32(conf.Width), float32(conf.Height)
|
|
obstacles := 0
|
|
maxPoints := conf.MaxPoints
|
|
mode := "deathmatch"
|
|
|
|
// here we determine if we are going to run with defaults or pick them off
|
|
// a posted json blob
|
|
if req.Method == "POST" {
|
|
body, err := ioutil.ReadAll(req.Body)
|
|
if err != nil {
|
|
log.Printf("unable to read request body:", err)
|
|
}
|
|
req.Body.Close()
|
|
cfg := struct {
|
|
Name string `json:"name"`
|
|
Config
|
|
}{}
|
|
err = json.Unmarshal(body, &cfg)
|
|
if err != nil {
|
|
if err := json.NewEncoder(w).Encode(NewFailure(err.Error())); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return
|
|
}
|
|
requested_game_name = cfg.Name
|
|
width = float32(cfg.Width)
|
|
height = float32(cfg.Height)
|
|
obstacles = cfg.Obstacles
|
|
maxPoints = cfg.MaxPoints
|
|
mode = cfg.Mode
|
|
}
|
|
|
|
g := games.get(requested_game_name)
|
|
if g == nil {
|
|
log.Printf("Game '%s' non-existant; making it now", requested_game_name)
|
|
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 {
|
|
log.Printf("Game '%s' already exists: %p", requested_game_name, g)
|
|
b, _ := json.Marshal(NewFailure("game already exists"))
|
|
http.Error(w, string(b), http.StatusConflict)
|
|
return
|
|
}
|
|
|
|
game_json := struct {
|
|
Id string `json:"id"`
|
|
}{
|
|
Id: g.id,
|
|
}
|
|
if err := json.NewEncoder(w).Encode(game_json); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func listGames(w http.ResponseWriter, req *http.Request) {
|
|
log.Println("games list requested")
|
|
games.RLock()
|
|
defer games.RUnlock()
|
|
type pout struct {
|
|
Name string `json:"name"`
|
|
Id string `json:"id"`
|
|
}
|
|
type gl struct {
|
|
Id string `json:"id"`
|
|
Players []pout `json:"players"`
|
|
}
|
|
ids := make([]gl, 0)
|
|
for id, g := range games.m {
|
|
players := make([]pout, 0)
|
|
// TODO - players instead of robots?
|
|
for p := range g.players {
|
|
for _, r := range p.Robots {
|
|
players = append(players, pout{
|
|
Name: r.Name,
|
|
Id: r.Id,
|
|
})
|
|
}
|
|
}
|
|
ids = append(ids, gl{
|
|
Id: id,
|
|
Players: players,
|
|
})
|
|
}
|
|
if err := json.NewEncoder(w).Encode(ids); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func gameStats(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 stats 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
|
|
}
|
|
g.stats.RLock()
|
|
defer g.stats.RUnlock()
|
|
if err := json.NewEncoder(w).Encode(g.stats.PlayerStats); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
b, _ := json.Marshal(NewFailure(err.Error()))
|
|
http.Error(w, string(b), http.StatusBadRequest)
|
|
return
|
|
}
|
|
games.Lock()
|
|
g, ok := games.m[key]
|
|
defer games.Unlock()
|
|
if !ok {
|
|
http.NotFound(w, req)
|
|
return
|
|
}
|
|
g.kill <- true
|
|
message := struct {
|
|
Ok bool `json:"ok"`
|
|
Message string `json:"message"`
|
|
}{
|
|
Ok: true,
|
|
Message: fmt.Sprintf("Successfully stopped game: %s", key),
|
|
}
|
|
if err := json.NewEncoder(w).Encode(message); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func killServer(w http.ResponseWriter, req *http.Request) {
|
|
if *profile != "" {
|
|
log.Print("trying to stop cpu profile")
|
|
pprof.StopCPUProfile()
|
|
log.Print("stopped cpu profile")
|
|
}
|
|
log.Fatal("shit got fucked up")
|
|
}
|
|
|
|
func index(w http.ResponseWriter, req *http.Request) {
|
|
log.Println("version requested")
|
|
version := struct {
|
|
Version string `json:"version"`
|
|
Name string `json:"name"`
|
|
}{
|
|
Version: "0.1.2",
|
|
Name: "Hackerbots",
|
|
}
|
|
if err := json.NewEncoder(w).Encode(version); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func getGameId(path string) (string, error) {
|
|
var err error
|
|
trimmed := strings.Trim(path, "/")
|
|
fullPath := strings.Split(trimmed, "/")
|
|
if len(fullPath) != 3 {
|
|
return "", errors.New("improperly formed url")
|
|
}
|
|
key := fullPath[2]
|
|
return key, err
|
|
}
|