You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
4.4 KiB

package conway
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"image/png"
"log"
"math/rand"
"net/http"
"net/url"
"strconv"
"time"
)
type WebGameParams struct {
Random bool
Height int
Width int
Xmul int
Ymul int
}
type WebGameState struct {
State *GameState `json:"state"`
Img string `json:"img"`
}
var jQueryMinJs = []byte("")
func init() {
rand.Seed(time.Now().UTC().UnixNano())
jqmin, err := base64.StdEncoding.DecodeString(JQUERY_MIN_JS)
if err != nil {
log.Fatal("Failed to decode internal jquery.min.js")
}
jQueryMinJs = append(jQueryMinJs, jqmin...)
}
func NewWebGameParams(q url.Values) *WebGameParams {
params := &WebGameParams{
Height: 50,
Width: 50,
Xmul: 10,
Ymul: 10,
}
if len(q.Get("random")) > 0 {
params.Random = true
}
h := q.Get("h")
if len(h) > 0 {
if i, err := strconv.Atoi(h); err == nil {
params.Height = i
}
}
w := q.Get("w")
if len(w) > 0 {
if i, err := strconv.Atoi(w); err == nil {
params.Width = i
}
}
xm := q.Get("xm")
if len(xm) > 0 {
if i, err := strconv.Atoi(xm); err == nil {
params.Xmul = i
}
}
ym := q.Get("ym")
if len(ym) > 0 {
if i, err := strconv.Atoi(ym); err == nil {
params.Ymul = i
}
}
return params
}
func handle405(method, uri string, w http.ResponseWriter) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte(fmt.Sprintf("Nope. %v not allowed at %v\n", method, uri)))
}
func handle500(err error, w http.ResponseWriter) {
log.Println("ERROR:", err)
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("We exploded!: %v\n", err)))
}
func HandleWebGameRoot(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" {
handle405(req.Method, req.RequestURI, w)
return
}
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write([]byte(GAME_OF_LIFE_INDEX_HTML))
return
}
func HandleWebGameStatic(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" {
handle405(req.Method, req.RequestURI, w)
return
}
if req.URL.Path == "/static/normalize.css" {
w.Header().Set("Content-Type", "text/css")
w.WriteHeader(http.StatusOK)
w.Write([]byte(NORMALIZE_CSS))
return
}
if req.URL.Path == "/static/jquery.min.js" {
w.Header().Set("Content-Type", "text/javascript")
w.WriteHeader(http.StatusOK)
w.Write(jQueryMinJs)
return
}
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(fmt.Sprintf("Don't have: %v\n", req.URL.Path)))
}
func HandleWebGameState(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" {
handle405(req.Method, req.RequestURI, w)
return
}
q := req.URL.Query()
params := NewWebGameParams(q)
if params.Random {
game := NewGameOfLife(params.Height, params.Width)
err := game.ImportRandomState()
if err != nil {
handle500(err, w)
return
}
img, err := game.Image(params.Xmul, params.Ymul)
if err != nil {
handle500(err, w)
return
}
log.Println("Serving random state image.")
var pngBuf bytes.Buffer
err = png.Encode(&pngBuf, img)
if err != nil {
handle500(err, w)
}
imgB64 := base64.StdEncoding.EncodeToString(pngBuf.Bytes())
webGameState := &WebGameState{State: game.State, Img: imgB64}
wgsJson, err := json.Marshal(webGameState)
if err != nil {
handle500(err, w)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(wgsJson)
return
}
rawState := q.Get("s")
if len(rawState) < 1 {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Missing query param \"s\".\n"))
return
}
//state := &GameState{}
//err := json.Unmarshal([]byte(rawState), state)
//if err != nil {
//w.Header().Set("Content-Type", "text/plain")
//w.WriteHeader(http.StatusBadRequest)
//w.Write([]byte(fmt.Sprintf("Invalid query param \"s\": %v\n", err)))
//return
//}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"state":null,"img":""}`))
return
}
func RunWebGame(address string, height, width, sleepMs int, mutate bool) (int, error) {
http.HandleFunc("/", HandleWebGameRoot)
http.HandleFunc("/state", HandleWebGameState)
http.HandleFunc("/static/", HandleWebGameStatic)
fmt.Printf("Serving on %v\n", address)
err := http.ListenAndServe(address, nil)
if err != nil {
return 1, err
}
return 0, nil
}