From 798cfb3c017ac3ecede0f31babddd9c40aeef778 Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Mon, 24 Jun 2019 16:16:54 -0400 Subject: [PATCH] add heroku API to repo --- api/Godeps/Godeps.json | 9 ++ api/Godeps/Readme | 5 + api/Procfile | 1 + api/main.go | 175 ++++++++++++++++++++++++++++++ favicon.ico => public/favicon.ico | Bin favicon.png => public/favicon.png | Bin fetch.js => public/fetch.js | 0 index.html => public/index.html | 0 style.css => public/style.css | 0 9 files changed, 190 insertions(+) create mode 100644 api/Godeps/Godeps.json create mode 100644 api/Godeps/Readme create mode 100644 api/Procfile create mode 100644 api/main.go rename favicon.ico => public/favicon.ico (100%) rename favicon.png => public/favicon.png (100%) rename fetch.js => public/fetch.js (100%) rename index.html => public/index.html (100%) rename style.css => public/style.css (100%) diff --git a/api/Godeps/Godeps.json b/api/Godeps/Godeps.json new file mode 100644 index 0000000..62a4552 --- /dev/null +++ b/api/Godeps/Godeps.json @@ -0,0 +1,9 @@ +{ + "ImportPath": "api", + "GoVersion": "go1.12", + "GodepVersion": "v80", + "Packages": [ + "./..." + ], + "Deps": [] +} diff --git a/api/Godeps/Readme b/api/Godeps/Readme new file mode 100644 index 0000000..4cdaa53 --- /dev/null +++ b/api/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/api/Procfile b/api/Procfile new file mode 100644 index 0000000..d9c24fc --- /dev/null +++ b/api/Procfile @@ -0,0 +1 @@ +web: api diff --git a/api/main.go b/api/main.go new file mode 100644 index 0000000..bf7a00f --- /dev/null +++ b/api/main.go @@ -0,0 +1,175 @@ +package main + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "log" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +type ServerDetailsStruct struct { + Players `json:"players"` + ServerQueue +} + +type ServerQueue struct { + CurrentPlayers int64 `json:"currentPlayers"` + CurrentQueue int64 `json:"currentQueue"` +} + +type Players []Player + +// Player details +type Player struct { + ID int64 `json:"id"` + Identifiers []string `json:"identifiers"` + Name string `json:"name"` + Ping int64 `json:"ping"` +} + +type Nopixeldata []NoPixelPlayer + +type NoPixelPlayer struct { + ID int `json:"id"` + Name string `json:"name"` + NoPixelID string `json:"noPixelID"` + SteamID string `json:"steamID"` + Twitch string `json:"twitch"` +} + +var ( + jsonGet = &http.Client{Timeout: 10 * time.Second} + // Using an environment variable to protect IP + ServerAddress = os.Getenv("SERVER_IP") + // ServerDetails struct to holds PlayerList & ServerDetails struct + ServerDetails = &ServerDetailsStruct{} + // NoPixelData struct + NoPixelData Nopixeldata +) + +// GetPlayerList sends HTTP get request to server to get list of players +func getPlayerList() (err error) { + server := strings.Builder{} + fmt.Fprintf(&server, "http://%s/players.json", ServerAddress) + req, err := jsonGet.Get(server.String()) + if err != nil { + return err + } + defer req.Body.Close() + err = json.NewDecoder(req.Body).Decode(&ServerDetails.Players) + if err != nil { + return err + } + return +} + +// GetServerQueueDetails opens UDP socket to FiveM Server and current players and queue from server details +func getServerQueueDetails() (err error) { + serverData := make([]byte, 256) + serverConnection, err := net.Dial("udp", ServerAddress) + defer serverConnection.Close() + if err != nil { + return err + } + fmt.Fprintf(serverConnection, "\xFF\xFF\xFF\xFFgetinfo f") + _, err = bufio.NewReader(serverConnection).Read(serverData) + + if err == nil { + serverData := bytes.Split(serverData, []byte("\n")) + serverDetails := bytes.Split(serverData[1], []byte("\\")) + serverQueue := bytes.FieldsFunc(serverDetails[12], func(c rune) bool { return c == '[' || c == ']' }) + + currentPlayerValues, _ := strconv.ParseInt(string(serverDetails[4]), 0, 64) + currentserverQueueValues, _ := strconv.ParseInt(string(serverQueue[0]), 0, 64) + ServerDetails.ServerQueue.CurrentPlayers = currentPlayerValues + if currentserverQueueValues >= 1 { + ServerDetails.ServerQueue.CurrentQueue = currentserverQueueValues + } + } else { + return err + } + return +} + +func steam64toSteam(input int64) (steamid string) { + legacySteamid := ((input - 76561197960265728) / 2) + steamid = fmt.Sprintf("STEAM_0:%d:%d", (input % 2), legacySteamid) + return +} + +func parsePlayers() (err error) { + var steamIDs []string + for i, v := range ServerDetails.Players { + steamIDs = nil + for ii, vv := range v.Identifiers { + if ii == 0 { + hexID := strings.Replace(vv, "steam:", "0x", -1) + steamID, _ := strconv.ParseInt(hexID, 0, 64) + s := strconv.FormatInt(steamID, 10) + p := getPlayerNoPixelInformation(s) + + steamIDs = append(steamIDs, + p.Name, + steam64toSteam(steamID), + fmt.Sprintf("%d", steamID), + p.Twitch, + p.NoPixelID) + } + } + ServerDetails.Players[i].Identifiers = steamIDs + } + return +} + +func loadPlayersJSON() (err error) { + jsonFile, err := jsonGet.Get("https://github.com/jakejarvis/npqueue/raw/master/directory.json") + if err != nil { + return + } + err = json.NewDecoder(jsonFile.Body).Decode(&NoPixelData) + if err != nil { + return err + } + return +} + +func getPlayerNoPixelInformation(id string) (p NoPixelPlayer) { + for i := range NoPixelData { + if NoPixelData[i].SteamID == id { + return NoPixelData[i] + } + } + return +} + +// List handler for Heroku /api/list route +func List(w http.ResponseWriter, r *http.Request) { + err := loadPlayersJSON() + if err != nil { + fmt.Fprintf(w, "failed to load JSON file %v", err) + return + } + getPlayerList() + getServerQueueDetails() + parsePlayers() + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") + json.NewEncoder(w).Encode(ServerDetails) +} + +func main() { + addr := ":" + os.Getenv("PORT") + + http.HandleFunc("/api/list", List) + log.Printf("Listening on %s...\n", addr) + if err := http.ListenAndServe(addr, nil); err != nil { + panic(err) + } + } \ No newline at end of file diff --git a/favicon.ico b/public/favicon.ico similarity index 100% rename from favicon.ico rename to public/favicon.ico diff --git a/favicon.png b/public/favicon.png similarity index 100% rename from favicon.png rename to public/favicon.png diff --git a/fetch.js b/public/fetch.js similarity index 100% rename from fetch.js rename to public/fetch.js diff --git a/index.html b/public/index.html similarity index 100% rename from index.html rename to public/index.html diff --git a/style.css b/public/style.css similarity index 100% rename from style.css rename to public/style.css