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) tick := conf.Tick // 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 { Width float32 `json:"width"` Height float32 `json:"height"` Name string `json:"name"` Tick int `json:"tick"` }{} 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 = cfg.Width height = cfg.Height tick = cfg.Tick } 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, tick) go g.run() games.add(g) } else { log.Printf("Game '%s' found: %p", requested_game_name, g) } 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.winners.RLock() defer g.winners.RUnlock() if err := json.NewEncoder(w).Encode(g.winners.m); 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 } log.Println("prekill") g.kill <- true log.Println("postkill") 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 }