@@ -12,3 +12,9 @@ | |||
# Output of the go coverage tool, specifically when used with LiteIDE | |||
*.out | |||
# go mod checksum | |||
go.sum | |||
# build binary | |||
./leaderboard-s | |||
@@ -0,0 +1,32 @@ | |||
// NGnius 2020-01-30 | |||
package main | |||
import ( | |||
"flag" | |||
"fmt" | |||
"os" | |||
) | |||
const ( | |||
defaultPort = "1337" | |||
defaultRoot = "." | |||
) | |||
var ( | |||
printVersionAndExit bool | |||
) | |||
func initArgs() { | |||
flag.StringVar(&port, "port", defaultPort, "Port to listen on") | |||
flag.StringVar(&root, "root", defaultRoot, "Root working directory") | |||
flag.BoolVar(&printVersionAndExit, "version", false, "Print version and exit") | |||
} | |||
func parseArgs() { | |||
flag.Parse() | |||
if printVersionAndExit { | |||
fmt.Println(Name + " v" + Version) | |||
os.Exit(0) | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
module git.exmods.org/NGnius/leadercraft-s | |||
go 1.13 |
@@ -0,0 +1,74 @@ | |||
// NGnius 2020-01-30 | |||
package main | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"net/http" | |||
"net/url" | |||
"strconv" | |||
) | |||
func boardHandler(w http.ResponseWriter, r *http.Request) { | |||
if r.Method != "GET" { | |||
w.WriteHeader(405) | |||
return | |||
} | |||
w.Header().Add("content-type", "application/json") | |||
w.Header().Add("Access-Control-Allow-Origin", "*") | |||
args := r.URL.Query() | |||
// check args pre-conditions | |||
if !checkArgExists(args, "board", w) { | |||
return | |||
} | |||
board := args.Get("board") | |||
if !checkArgExists(args, "count", w) || !checkArgInt(args, "count", w, 0) { | |||
return | |||
} | |||
count, _ := strconv.Atoi(args.Get("count")) | |||
if !checkArgExists(args, "start", w) || !checkArgInt(args, "start", w, 0) { | |||
return | |||
} | |||
start, _ := strconv.Atoi(args.Get("start")) | |||
// execute query | |||
result := NewResult("", r.URL.String()) | |||
b, ok := boards[board] | |||
if !ok { | |||
w.WriteHeader(404) | |||
return | |||
} | |||
if len(b.Entries) < start+count { | |||
count = len(b.Entries) - start | |||
} | |||
// result.Items = b.Entries[start : start+count] | |||
for _, entry := range b.Entries[start : start+count] { | |||
item := entry | |||
result.Items = append(result.Items, &item) | |||
} | |||
result.Query = fmt.Sprintf("load board[%s] from %d to %d", board, start, count+start) | |||
result.Complete() | |||
data, err := json.Marshal(result) | |||
if err != nil { | |||
w.WriteHeader(500) | |||
return | |||
} | |||
w.Write(data) | |||
} | |||
func checkArgExists(values url.Values, key string, w http.ResponseWriter) (ok bool) { | |||
ok = values.Get(key) != "" | |||
if !ok { | |||
w.WriteHeader(400) | |||
} | |||
return | |||
} | |||
func checkArgInt(values url.Values, key string, w http.ResponseWriter, min int) (ok bool) { | |||
intVal, err := strconv.Atoi(values.Get(key)) | |||
ok = err == nil && intVal >= min | |||
if !ok { | |||
w.WriteHeader(400) | |||
} | |||
return | |||
} |
@@ -0,0 +1,60 @@ | |||
// NGnius 2020-01-30 | |||
package main | |||
import ( | |||
"strconv" | |||
"time" | |||
) | |||
// Board a leaderboard | |||
type Board struct { | |||
ID int64 | |||
Entries []Entry | |||
Name string | |||
} | |||
// Entry an entry in a leaderboard | |||
type Entry struct { | |||
ID int64 | |||
Rank int64 | |||
Score int64 | |||
PlayerName string | |||
PlayerURL string | |||
} | |||
// Player a player | |||
type Player struct { | |||
ID int64 | |||
Name string | |||
Entries []Entry | |||
} | |||
// URL get the player's URL | |||
func (p *Player) URL() string { | |||
return "/player/" + strconv.Itoa(int(p.ID)) | |||
} | |||
// Result a query result | |||
type Result struct { | |||
Items []interface{} | |||
Elapsed int64 | |||
Count int | |||
Query string | |||
URL string | |||
Start time.Time | |||
} | |||
// NewResult build a query struct | |||
func NewResult(q string, url string) (r Result) { | |||
r.Start = time.Now() | |||
r.Query = q | |||
r.URL = url | |||
return | |||
} | |||
// Complete finish the result | |||
func (r *Result) Complete() { | |||
r.Count = len(r.Items) | |||
r.Elapsed = time.Since(r.Start).Milliseconds() | |||
} |
@@ -0,0 +1,58 @@ | |||
// NGnius 2020-01-30 | |||
package main // leadercraft-server | |||
import ( | |||
"fmt" | |||
"net/http" | |||
) | |||
const ( | |||
// Version the current version | |||
Version = "0.1" | |||
// Name the program name | |||
Name = "leadercraft-s" | |||
) | |||
var ( | |||
server *http.Server | |||
handler http.Handler | |||
port string | |||
root string | |||
boards map[string]Board | |||
) | |||
func init() { | |||
initArgs() | |||
serverMux := http.NewServeMux() | |||
serverMux.HandleFunc("/load", boardHandler) | |||
handler = serverMux | |||
boards = make(map[string]Board) | |||
// test data | |||
boards["test1"] = Board{ | |||
ID: 1, | |||
Entries: []Entry{ | |||
Entry{ID: 1, Rank: 1, Score: 1000, PlayerName: "NGnius", PlayerURL: "/player/1"}, | |||
Entry{ID: 2, Rank: 2, Score: 900, PlayerName: "Also NGnius", PlayerURL: "/player/2"}, | |||
Entry{ID: 4, Rank: 3, Score: 400, PlayerName: ".xX||eDgY TeeNaGeR||Xx.", PlayerURL: "/player/3"}, | |||
Entry{ID: 3, Rank: 4, Score: 350, PlayerName: "New username who dis?", PlayerURL: "/player/4"}, | |||
Entry{ID: 7, Rank: 5, Score: 350, PlayerName: "Extremely Ridiculously Long Name Please don't break things", PlayerURL: "/player/5"}, | |||
Entry{ID: 5, Rank: 6, Score: 250, PlayerName: "P|P3 |o|z", PlayerURL: "/player/3333"}, | |||
Entry{ID: 6, Rank: 7, Score: 200, PlayerName: "Simon", PlayerURL: "/player/456"}, | |||
Entry{ID: 24, Rank: 8, Score: 175, PlayerName: "Zettagram.com", PlayerURL: "/player/32"}, | |||
Entry{ID: 42, Rank: 9, Score: 150, PlayerName: "The Doctor", PlayerURL: "/player/9283"}, | |||
Entry{ID: 43, Rank: 10, Score: 140, PlayerName: "Marvin the Paranoid Android", PlayerURL: "/player/6"}, | |||
Entry{ID: 8, Rank: 11, Score: 100, PlayerName: "IDK HOW", PlayerURL: "/player/666"}, | |||
}, | |||
} | |||
} | |||
func main() { | |||
parseArgs() | |||
server = &http.Server{ | |||
Addr: ":" + port, | |||
Handler: handler, | |||
} | |||
fmt.Println("Starting on " + server.Addr) | |||
fmt.Println(server.ListenAndServe()) | |||
} |