big refactor
Some checks failed
Dynamic Branch Deploy / build-and-deploy (push) Failing after 48s
Some checks failed
Dynamic Branch Deploy / build-and-deploy (push) Failing after 48s
This commit is contained in:
233
handlers.go
Normal file
233
handlers.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(defaultConfig)
|
||||
}
|
||||
|
||||
func handleStart(w http.ResponseWriter, r *http.Request) {
|
||||
sessionID := uuid.New().String()
|
||||
rawSeed := time.Now().UnixNano()
|
||||
seed32 := uint32(rawSeed)
|
||||
|
||||
emptyObs, _ := json.Marshal([]ActiveObstacle{})
|
||||
|
||||
err := rdb.HSet(ctx, "session:"+sessionID, map[string]interface{}{
|
||||
"seed": seed32,
|
||||
"rng_state": seed32,
|
||||
"score": 0,
|
||||
"is_dead": 0,
|
||||
"pos_y": PlayerYBase,
|
||||
"vel_y": 0.0,
|
||||
"obstacles": string(emptyObs),
|
||||
}).Err()
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "DB Error", 500)
|
||||
return
|
||||
}
|
||||
rdb.Expire(ctx, "session:"+sessionID, 4000*time.Hour)
|
||||
|
||||
json.NewEncoder(w).Encode(StartResponse{SessionID: sessionID, Seed: seed32})
|
||||
}
|
||||
|
||||
func handleValidate(w http.ResponseWriter, r *http.Request) {
|
||||
var req ValidateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Bad Request", 400)
|
||||
return
|
||||
}
|
||||
|
||||
key := "session:" + req.SessionID
|
||||
vals, err := rdb.HGetAll(ctx, key).Result()
|
||||
if err != nil || len(vals) == 0 {
|
||||
http.Error(w, "Session invalid", 401)
|
||||
return
|
||||
}
|
||||
|
||||
if vals["is_dead"] == "1" {
|
||||
json.NewEncoder(w).Encode(ValidateResponse{Status: "dead", VerifiedScore: 0})
|
||||
return
|
||||
}
|
||||
|
||||
// ---> HIER RUFEN WIR JETZT DIE SIMULATION AUF <---
|
||||
isDead, score, obstacles := simulateChunk(req.SessionID, req.Inputs, req.TotalTicks, vals)
|
||||
|
||||
status := "alive"
|
||||
if isDead {
|
||||
status = "dead"
|
||||
rdb.HSet(ctx, key, "is_dead", 1)
|
||||
}
|
||||
rdb.Expire(ctx, key, 4000*time.Hour)
|
||||
|
||||
json.NewEncoder(w).Encode(ValidateResponse{
|
||||
Status: status,
|
||||
VerifiedScore: score,
|
||||
ServerObs: obstacles,
|
||||
})
|
||||
}
|
||||
|
||||
func handleSubmitName(w http.ResponseWriter, r *http.Request) {
|
||||
var req SubmitNameRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Bad Request", 400)
|
||||
return
|
||||
}
|
||||
|
||||
safeName := html.EscapeString(req.Name)
|
||||
sessionKey := "session:" + req.SessionID
|
||||
scoreVal, err := rdb.HGet(ctx, sessionKey, "score").Result()
|
||||
if err != nil {
|
||||
http.Error(w, "Session expired", 404)
|
||||
return
|
||||
}
|
||||
scoreInt, _ := strconv.Atoi(scoreVal)
|
||||
|
||||
claimCode := generateClaimCode()
|
||||
timestamp := time.Now().Format("02.01.2006 15:04")
|
||||
|
||||
rdb.HSet(ctx, sessionKey, map[string]interface{}{
|
||||
"name": safeName,
|
||||
"claim_code": claimCode,
|
||||
"created_at": timestamp,
|
||||
})
|
||||
|
||||
rdb.ZAdd(ctx, "leaderboard:unverified", redis.Z{
|
||||
Score: float64(scoreInt),
|
||||
Member: req.SessionID,
|
||||
})
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(SubmitResponse{ClaimCode: claimCode})
|
||||
}
|
||||
|
||||
func handleLeaderboard(w http.ResponseWriter, r *http.Request) {
|
||||
mySessionID := r.URL.Query().Get("sessionId")
|
||||
targetKey := "leaderboard:public"
|
||||
|
||||
var entries []LeaderboardEntry
|
||||
top3, _ := rdb.ZRevRangeWithScores(ctx, targetKey, 0, 2).Result()
|
||||
|
||||
for i, z := range top3 {
|
||||
rank := int64(i + 1)
|
||||
sid := z.Member.(string)
|
||||
name, _ := rdb.HGet(ctx, "session:"+sid, "name").Result()
|
||||
if name == "" {
|
||||
name = "Unbekannt"
|
||||
}
|
||||
entries = append(entries, LeaderboardEntry{
|
||||
Rank: rank, Name: name, Score: int(z.Score), IsMe: (sid == mySessionID),
|
||||
})
|
||||
}
|
||||
|
||||
if mySessionID != "" {
|
||||
myRank, err := rdb.ZRevRank(ctx, targetKey, mySessionID).Result()
|
||||
if err == nil && myRank > 2 {
|
||||
start := myRank - 1
|
||||
stop := myRank + 1
|
||||
neighbors, _ := rdb.ZRevRangeWithScores(ctx, targetKey, start, stop).Result()
|
||||
for i, z := range neighbors {
|
||||
rank := start + int64(i) + 1
|
||||
sid := z.Member.(string)
|
||||
name, _ := rdb.HGet(ctx, "session:"+sid, "name").Result()
|
||||
if name == "" {
|
||||
name = "Unbekannt"
|
||||
}
|
||||
entries = append(entries, LeaderboardEntry{
|
||||
Rank: rank, Name: name, Score: int(z.Score), IsMe: (sid == mySessionID),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(entries)
|
||||
}
|
||||
|
||||
func handleAdminPage(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "./secure/admin.html")
|
||||
}
|
||||
|
||||
func handleAdminList(w http.ResponseWriter, r *http.Request) {
|
||||
listType := r.URL.Query().Get("type")
|
||||
redisKey := "leaderboard:unverified"
|
||||
if listType == "public" {
|
||||
redisKey = "leaderboard:public"
|
||||
}
|
||||
|
||||
vals, _ := rdb.ZRevRangeWithScores(ctx, redisKey, 0, -1).Result()
|
||||
var adminList []AdminEntry
|
||||
|
||||
for _, z := range vals {
|
||||
sid := z.Member.(string)
|
||||
info, _ := rdb.HGetAll(ctx, "session:"+sid).Result()
|
||||
name := info["name"]
|
||||
if name == "" {
|
||||
name = "Unbekannt"
|
||||
}
|
||||
adminList = append(adminList, AdminEntry{
|
||||
SessionID: sid, Name: name, Score: int(z.Score),
|
||||
Code: info["claim_code"], Time: info["created_at"],
|
||||
})
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(adminList)
|
||||
}
|
||||
|
||||
func handleAdminAction(w http.ResponseWriter, r *http.Request) {
|
||||
var req AdminActionRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Bad Request", 400)
|
||||
return
|
||||
}
|
||||
if req.Action == "approve" {
|
||||
score, err := rdb.ZScore(ctx, "leaderboard:unverified", req.SessionID).Result()
|
||||
if err == nil {
|
||||
rdb.ZAdd(ctx, "leaderboard:public", redis.Z{Score: score, Member: req.SessionID})
|
||||
rdb.ZRem(ctx, "leaderboard:unverified", req.SessionID)
|
||||
}
|
||||
} else if req.Action == "delete" {
|
||||
rdb.ZRem(ctx, "leaderboard:unverified", req.SessionID)
|
||||
rdb.ZRem(ctx, "leaderboard:public", req.SessionID)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func handleClaimDelete(w http.ResponseWriter, r *http.Request) {
|
||||
var req ClaimDeleteRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Bad Request", 400)
|
||||
return
|
||||
}
|
||||
sessionKey := "session:" + req.SessionID
|
||||
realCode, err := rdb.HGet(ctx, sessionKey, "claim_code").Result()
|
||||
|
||||
if err != nil || realCode != req.ClaimCode {
|
||||
http.Error(w, "Error", 403)
|
||||
return
|
||||
}
|
||||
rdb.ZRem(ctx, "leaderboard:unverified", req.SessionID)
|
||||
rdb.ZRem(ctx, "leaderboard:public", req.SessionID)
|
||||
rdb.HDel(ctx, sessionKey, "name")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func generateClaimCode() string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, 8)
|
||||
for i := range b {
|
||||
b[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user