|
- // NGnius 2020-02-27
-
- package main
-
- import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
- "os/signal"
- "path/filepath"
- )
-
- const (
- defaultPassword = ""
- defaultEntryURL = "http://localhost:1337/record"
- defaultPort = "9000"
- defaultDir = "criterias"
- )
-
- var (
- // cli params
- password string
- entryURL string
- port string
- dir string
- // internal
- server *http.Server
- client *http.Client
- isClosing bool
- referenceCriteria map[string]Criteria
- )
-
- // json structs
-
- // NewEntryJSON a new entry to be saved to a leaderboard
- type NewEntryJSON struct { // from leadercraft-s
- Score int64
- PlayerID int64
- BoardID int64
- Password string
- }
-
- // Criteria a set of values that must be met
- type Criteria struct {
- Location [2][3]float64 // (min coords (x,y,z), max coords (x,y,z))
- Time int64 // time since start of game (seconds)
- GameID int64 // game/board id in leadercraft-s and in Gamecraft (workshop ID?)
- PlayerID int64 // player id in leadercraft-s and in Gamecraft (steam ID)
- Coefficient float64 // coefficient for calculating the score
- Complete bool // game has been completed
- Points int64 // points scored in game
- ScoreMode string // score calculation mode
- }
-
- func (c *Criteria) Meets(c2 *Criteria) bool {
- meets := false
- if c2.Location[0][0] != 0.0 && c2.Location[0][1] != 0.0 && c2.Location[0][2] != 0.0 &&
- c2.Location[1][0] != 0.0 && c2.Location[1][1] != 0.0 && c2.Location[1][2] != 0.0 {
- // criteria is location-based
- meets = c.Location[0][0] >= c2.Location[0][0] && c.Location[0][1] >= c2.Location[0][1] && c.Location[0][2] >= c2.Location[0][2]
- meets = meets && c.Location[1][0] <= c2.Location[1][0] && c.Location[1][1] <= c2.Location[1][1] && c.Location[1][2] <= c2.Location[1][2]
- return meets
- }
- if c2.Complete {
- return c.Complete && (c.Points >= c2.Points)
- }
- return meets
- }
-
- func (c *Criteria) Score(c2 *Criteria) int64 {
- if c2.ScoreMode == "time" {
- time := float64(c.Time)
- if time < 1 {
- time = 1
- }
- return int64(c2.Coefficient / float64(c.Time))
- }
- if c2.ScoreMode == "points" {
- return int64(c2.Coefficient * float64(c.Points))
- }
- return 0
- }
-
- func init() {
- flag.StringVar(&password, "entry-pwd", defaultPassword, "Password for new entry POST requests")
- flag.StringVar(&entryURL, "url", defaultEntryURL, "URL for new entry POST requests")
- flag.StringVar(&port, "port", defaultPort, "Port to listen on")
- flag.StringVar(&dir, "dir", defaultDir, "Working directory")
- }
-
- func main() {
- flag.Parse()
- serverMux := http.NewServeMux()
- serverMux.HandleFunc("/criteria", criteriaHandler)
- signalChan := make(chan os.Signal)
- signal.Notify(signalChan, os.Interrupt)
- go func() {
- s := <-signalChan
- fmt.Println("Received terminate signal " + s.String())
- isClosing = true
- server.Close()
- }()
- server = &http.Server{
- Addr: ":" + port,
- Handler: serverMux,
- }
- client = &http.Client{}
- fmt.Println("Starting on " + server.Addr)
- err := server.ListenAndServe()
- if err != nil && !isClosing {
- fmt.Println(err)
- }
- }
-
- // criteria POST request handler
- // this also sends a new entry to leadercraft-s when the criteria is met
- func criteriaHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != "POST" {
- w.WriteHeader(405)
- return
- }
- authHeader := r.Header.Get("Authorization")
- w.Header().Add("Content-Type", "application/json")
- //w.Header().Add("Access-Control-Allow-Origin", "*")
- data, readErr := ioutil.ReadAll(r.Body)
- if readErr != nil {
- // body could not be read properly
- w.WriteHeader(500)
- return
- }
- reqCriteria := &Criteria{}
- unmarshErr := json.Unmarshal(data, reqCriteria)
- if unmarshErr != nil {
- // body could not be interpreted as json
- w.WriteHeader(400)
- return
- }
- if reqCriteria.GameID < 2 {
- //fmt.Println("404 -- GameID too low")
- w.WriteHeader(404)
- return
- }
- criteriaFilename := filepath.Join(dir, fmt.Sprintf("criteria-%d.json", reqCriteria.GameID))
- //fmt.Println(criteriaFilename)
- realCriteria := &Criteria{}
- realCriteriaF, openErr := os.Open(criteriaFilename)
- if openErr != nil {
- // file not found (or not accessible)
- //fmt.Println("404 -- criteria file not accessible: %s", openErr.Error())
- w.WriteHeader(404)
- return
- }
- realCritData, realCritReadErr := ioutil.ReadAll(realCriteriaF)
- if realCritReadErr != nil {
- // internal read error
- w.WriteHeader(500)
- return
- }
- unmarshCritErr := json.Unmarshal(realCritData, realCriteria)
- if unmarshCritErr != nil {
- // criteria file is invalid json
- //fmt.Printf("404 -- Invalid criteria file json: %s", unmarshCritErr.Error())
- w.WriteHeader(404)
- return
- }
- // TODO check if criteria matches
- if !reqCriteria.Meets(realCriteria) {
- // if criteria does not match, stop
- //fmt.Println("400 -- Criteria does not meet required criteria")
- w.WriteHeader(400)
- return
- }
- // if criteria matches, send new entry to leadercraft-s
- entry := NewEntryJSON{
- Score: reqCriteria.Score(realCriteria),
- PlayerID: reqCriteria.PlayerID,
- BoardID: realCriteria.GameID,
- Password: password,
- }
- echoData, marshErr := json.Marshal(entry)
- if marshErr != nil {
- //fmt.Println("500 -- Unable to marshal entry into JSON for leaderboard-s endpoint")
- w.WriteHeader(500)
- return
- }
- echoBody := bytes.NewReader(echoData)
- entryReq, reqErr := http.NewRequest("POST", entryURL, echoBody)
- if reqErr != nil {
- // malformed request parameters
- //fmt.Println("500 -- Malformed request detected during initialization")
- w.WriteHeader(500)
- return
- }
- entryReq.Header.Add("Authorization", authHeader)
- entryReq.Header.Add("Content-Type", "application/json")
- echoResp, postErr := client.Do(entryReq)
- if postErr != nil {
- // bad communication or malformed request
- //fmt.Println("500 -- Bad communication for leadercraft-s")
- w.WriteHeader(500)
- return
- }
- // echo new entry request response to original sender
- w.WriteHeader(echoResp.StatusCode)
- echoRespData, echoReadErr := ioutil.ReadAll(echoResp.Body)
- if echoReadErr != nil {
- // body read error (should never occur)
- //fmt.Println("!!! Error reading response body from leadercraft-s")
- return
- }
- w.Write(echoRespData)
- }
|