add a lot debug
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 55s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 55s
This commit is contained in:
181
main.go
181
main.go
@@ -16,16 +16,23 @@ import (
|
|||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 1. KONSTANTEN
|
||||||
|
// ==========================================
|
||||||
const (
|
const (
|
||||||
Gravity = 0.6
|
Gravity = 0.6
|
||||||
JumpPower = -12.0
|
JumpPower = -12.0
|
||||||
GroundY = 350.0
|
GroundY = 350.0
|
||||||
PlayerHeight = 50.0
|
PlayerHeight = 50.0
|
||||||
PlayerYBase = GroundY - PlayerHeight
|
PlayerYBase = GroundY - PlayerHeight // 300.0
|
||||||
|
|
||||||
GameSpeed = 5.0
|
GameSpeed = 5.0
|
||||||
GameWidth = 800.0
|
GameWidth = 800.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 2. GLOBALE VARIABLEN
|
||||||
|
// ==========================================
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
rdb *redis.Client
|
rdb *redis.Client
|
||||||
@@ -34,6 +41,10 @@ var (
|
|||||||
adminPass string
|
adminPass string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 3. STRUCTS
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
type ObstacleDef struct {
|
type ObstacleDef struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Width float64 `json:"width"`
|
Width float64 `json:"width"`
|
||||||
@@ -114,11 +125,16 @@ type ClaimDeleteRequest struct {
|
|||||||
ClaimCode string `json:"claimCode"`
|
ClaimCode string `json:"claimCode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 4. PSEUDO RNG
|
||||||
|
// ==========================================
|
||||||
type PseudoRNG struct {
|
type PseudoRNG struct {
|
||||||
State uint32
|
State uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRNG(seed int64) *PseudoRNG {
|
func NewRNG(seed int64) *PseudoRNG {
|
||||||
|
// DEBUG: RNG Init
|
||||||
|
// log.Printf("[RNG] Init mit Seed: %d", seed)
|
||||||
return &PseudoRNG{State: uint32(seed)}
|
return &PseudoRNG{State: uint32(seed)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +156,9 @@ func (r *PseudoRNG) PickDef(defs []ObstacleDef) *ObstacleDef {
|
|||||||
return &defs[idx]
|
return &defs[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 5. HELPER
|
||||||
|
// ==========================================
|
||||||
func getEnv(key, fallback string) string {
|
func getEnv(key, fallback string) string {
|
||||||
if value, ok := os.LookupEnv(key); ok {
|
if value, ok := os.LookupEnv(key); ok {
|
||||||
return value
|
return value
|
||||||
@@ -147,6 +166,27 @@ func getEnv(key, fallback string) string {
|
|||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseOr(s string, def float64) float64 {
|
||||||
|
if s == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateClaimCode() string {
|
||||||
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
b := make([]byte, 8)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[rand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware für Basic Auth
|
||||||
func BasicAuth(next http.HandlerFunc) http.HandlerFunc {
|
func BasicAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
user, pass, ok := r.BasicAuth()
|
user, pass, ok := r.BasicAuth()
|
||||||
@@ -159,45 +199,60 @@ func BasicAuth(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middleware für Request Logging (DAMIT DU SIEHST OB REQUESTS ANKOMMEN)
|
||||||
|
func LogRequest(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
log.Printf("➡ [REQ] %s %s von %s", r.Method, r.URL.Path, r.RemoteAddr)
|
||||||
|
next(w, r)
|
||||||
|
log.Printf("⬅ [RES] %s %s fertig in %v", r.Method, r.URL.Path, time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 6. MAIN
|
||||||
|
// ==========================================
|
||||||
func main() {
|
func main() {
|
||||||
|
// Config laden
|
||||||
redisAddr := getEnv("REDIS_ADDR", "localhost:6379")
|
redisAddr := getEnv("REDIS_ADDR", "localhost:6379")
|
||||||
adminUser = getEnv("ADMIN_USER", "lehrer")
|
adminUser = getEnv("ADMIN_USER", "lehrer")
|
||||||
adminPass = getEnv("ADMIN_PASS", "geheim123")
|
adminPass = getEnv("ADMIN_PASS", "geheim123")
|
||||||
|
|
||||||
|
log.Printf("🔧 CONFIG: Redis=%s | AdminUser=%s", redisAddr, adminUser)
|
||||||
|
|
||||||
|
// Redis verbinden
|
||||||
|
log.Println("🔌 Verbinde zu Redis...")
|
||||||
rdb = redis.NewClient(&redis.Options{Addr: redisAddr})
|
rdb = redis.NewClient(&redis.Options{Addr: redisAddr})
|
||||||
if _, err := rdb.Ping(ctx).Result(); err != nil {
|
|
||||||
log.Fatal(err)
|
pong, err := rdb.Ping(ctx).Result()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("❌ REDIS ERROR: %v", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("✅ Redis verbunden: %s", pong)
|
||||||
|
|
||||||
initGameConfig()
|
initGameConfig()
|
||||||
|
|
||||||
|
// File Server
|
||||||
fs := http.FileServer(http.Dir("./static"))
|
fs := http.FileServer(http.Dir("./static"))
|
||||||
http.Handle("/", fs)
|
http.Handle("/", fs)
|
||||||
|
|
||||||
http.HandleFunc("/api/config", handleConfig)
|
// API Routes (mit Logging Wrapper)
|
||||||
http.HandleFunc("/api/start", handleStart)
|
http.HandleFunc("/api/config", LogRequest(handleConfig))
|
||||||
http.HandleFunc("/api/validate", handleValidate)
|
http.HandleFunc("/api/start", LogRequest(handleStart))
|
||||||
http.HandleFunc("/api/submit-name", handleSubmitName)
|
http.HandleFunc("/api/validate", LogRequest(handleValidate)) // Hier passiert die Magie
|
||||||
http.HandleFunc("/api/leaderboard", handleLeaderboard)
|
http.HandleFunc("/api/submit-name", LogRequest(handleSubmitName))
|
||||||
http.HandleFunc("/api/claim/delete", handleClaimDelete)
|
http.HandleFunc("/api/leaderboard", LogRequest(handleLeaderboard))
|
||||||
|
http.HandleFunc("/api/claim/delete", LogRequest(handleClaimDelete))
|
||||||
|
|
||||||
|
// Admin Routes
|
||||||
http.HandleFunc("/admin", BasicAuth(handleAdminPage))
|
http.HandleFunc("/admin", BasicAuth(handleAdminPage))
|
||||||
http.HandleFunc("/api/admin/list", BasicAuth(handleAdminList))
|
http.HandleFunc("/api/admin/list", BasicAuth(handleAdminList))
|
||||||
http.HandleFunc("/api/admin/action", BasicAuth(handleAdminAction))
|
http.HandleFunc("/api/admin/action", BasicAuth(handleAdminAction))
|
||||||
|
|
||||||
log.Println("Server läuft auf Port 8080")
|
log.Println("🦖 Server läuft auf Port 8080")
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateClaimCode() string {
|
|
||||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
||||||
b := make([]byte, 8)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = charset[rand.Intn(len(charset))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGameConfig() {
|
func initGameConfig() {
|
||||||
defaultConfig = GameConfig{
|
defaultConfig = GameConfig{
|
||||||
Obstacles: []ObstacleDef{
|
Obstacles: []ObstacleDef{
|
||||||
@@ -206,10 +261,15 @@ func initGameConfig() {
|
|||||||
{ID: "trashcan", Width: 25, Height: 35, Color: "#555", Image: "trash.png"},
|
{ID: "trashcan", Width: 25, Height: 35, Color: "#555", Image: "trash.png"},
|
||||||
{ID: "eraser", Width: 30, Height: 20, Color: "#fff", Image: "eraser.png", YOffset: 45.0},
|
{ID: "eraser", Width: 30, Height: 20, Color: "#fff", Image: "eraser.png", YOffset: 45.0},
|
||||||
},
|
},
|
||||||
Backgrounds: []string{"background.jpg"},
|
Backgrounds: []string{"background.png"},
|
||||||
}
|
}
|
||||||
|
log.Println("✅ Game Config initialisiert")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 7. HANDLER (MIT DEBUG LOGS)
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
func handleConfig(w http.ResponseWriter, r *http.Request) {
|
func handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(defaultConfig)
|
json.NewEncoder(w).Encode(defaultConfig)
|
||||||
@@ -217,25 +277,34 @@ func handleConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func handleStart(w http.ResponseWriter, r *http.Request) {
|
func handleStart(w http.ResponseWriter, r *http.Request) {
|
||||||
sessionID := uuid.New().String()
|
sessionID := uuid.New().String()
|
||||||
|
|
||||||
|
// Seed erstellen (32-Bit sicher)
|
||||||
rawSeed := time.Now().UnixNano()
|
rawSeed := time.Now().UnixNano()
|
||||||
seed32 := uint32(rawSeed)
|
seed32 := uint32(rawSeed)
|
||||||
|
|
||||||
|
// Initiale Hindernisse (leer)
|
||||||
emptyObs, _ := json.Marshal([]ActiveObstacle{})
|
emptyObs, _ := json.Marshal([]ActiveObstacle{})
|
||||||
|
|
||||||
|
log.Printf("🆕 START SESSION: %s | Seed: %d", sessionID, seed32)
|
||||||
|
|
||||||
|
// In Redis speichern
|
||||||
err := rdb.HSet(ctx, "session:"+sessionID, map[string]interface{}{
|
err := rdb.HSet(ctx, "session:"+sessionID, map[string]interface{}{
|
||||||
"seed": seed32,
|
"seed": seed32,
|
||||||
"rng_state": seed32,
|
"rng_state": seed32,
|
||||||
"score": 0,
|
"score": 0,
|
||||||
"is_dead": 0,
|
"is_dead": 0,
|
||||||
"pos_y": PlayerYBase,
|
"pos_y": PlayerYBase, // WICHTIG: Start auf dem Boden (300.0)
|
||||||
"vel_y": 0.0,
|
"vel_y": 0.0,
|
||||||
"obstacles": string(emptyObs),
|
"obstacles": string(emptyObs),
|
||||||
}).Err()
|
}).Err()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("❌ REDIS WRITE ERROR bei Start: %v", err)
|
||||||
http.Error(w, "DB Error", 500)
|
http.Error(w, "DB Error", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expire setzen
|
||||||
rdb.Expire(ctx, "session:"+sessionID, 1*time.Hour)
|
rdb.Expire(ctx, "session:"+sessionID, 1*time.Hour)
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(StartResponse{SessionID: sessionID, Seed: seed32})
|
json.NewEncoder(w).Encode(StartResponse{SessionID: sessionID, Seed: seed32})
|
||||||
@@ -244,29 +313,38 @@ func handleStart(w http.ResponseWriter, r *http.Request) {
|
|||||||
func handleValidate(w http.ResponseWriter, r *http.Request) {
|
func handleValidate(w http.ResponseWriter, r *http.Request) {
|
||||||
var req ValidateRequest
|
var req ValidateRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
log.Printf("⚠️ Validate Decode Error: %v", err)
|
||||||
http.Error(w, "Bad Request", 400)
|
http.Error(w, "Bad Request", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
key := "session:" + req.SessionID
|
key := "session:" + req.SessionID
|
||||||
|
|
||||||
|
// Alle Daten holen
|
||||||
vals, err := rdb.HGetAll(ctx, key).Result()
|
vals, err := rdb.HGetAll(ctx, key).Result()
|
||||||
if err != nil || len(vals) == 0 {
|
if err != nil || len(vals) == 0 {
|
||||||
|
log.Printf("⚠️ Session nicht gefunden oder leer: %s", req.SessionID)
|
||||||
http.Error(w, "Session invalid", 401)
|
http.Error(w, "Session invalid", 401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ob schon tot
|
||||||
if vals["is_dead"] == "1" {
|
if vals["is_dead"] == "1" {
|
||||||
|
// log.Printf("💀 Validierung abgelehnt: Spieler %s ist bereits tot.", req.SessionID)
|
||||||
json.NewEncoder(w).Encode(ValidateResponse{Status: "dead", VerifiedScore: 0})
|
json.NewEncoder(w).Encode(ValidateResponse{Status: "dead", VerifiedScore: 0})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State parsen
|
||||||
posY := parseOr(vals["pos_y"], PlayerYBase)
|
posY := parseOr(vals["pos_y"], PlayerYBase)
|
||||||
velY := parseOr(vals["vel_y"], 0.0)
|
velY := parseOr(vals["vel_y"], 0.0)
|
||||||
score := int(parseOr(vals["score"], 0))
|
score := int(parseOr(vals["score"], 0))
|
||||||
|
|
||||||
rngStateVal, _ := strconv.ParseInt(vals["rng_state"], 10, 64)
|
rngStateVal, _ := strconv.ParseInt(vals["rng_state"], 10, 64)
|
||||||
|
|
||||||
|
// RNG initialisieren
|
||||||
rng := NewRNG(rngStateVal)
|
rng := NewRNG(rngStateVal)
|
||||||
|
|
||||||
|
// Hindernisse laden
|
||||||
var obstacles []ActiveObstacle
|
var obstacles []ActiveObstacle
|
||||||
if val, ok := vals["obstacles"]; ok && val != "" {
|
if val, ok := vals["obstacles"]; ok && val != "" {
|
||||||
json.Unmarshal([]byte(val), &obstacles)
|
json.Unmarshal([]byte(val), &obstacles)
|
||||||
@@ -274,12 +352,16 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
obstacles = []ActiveObstacle{}
|
obstacles = []ActiveObstacle{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log.Printf("🎮 SIMULATION START (%s) | Ticks: %d | PosY: %.2f | Obstacles: %d", req.SessionID, req.TotalTicks, posY, len(obstacles))
|
||||||
|
|
||||||
playerDead := false
|
playerDead := false
|
||||||
|
|
||||||
|
// --- SIMULATION LOOP ---
|
||||||
for i := 0; i < req.TotalTicks; i++ {
|
for i := 0; i < req.TotalTicks; i++ {
|
||||||
|
|
||||||
|
// A. INPUT
|
||||||
didJump := false
|
didJump := false
|
||||||
isCrouching := false
|
isCrouching := false
|
||||||
|
|
||||||
for _, inp := range req.Inputs {
|
for _, inp := range req.Inputs {
|
||||||
if inp.Tick == i {
|
if inp.Tick == i {
|
||||||
if inp.Act == "JUMP" {
|
if inp.Act == "JUMP" {
|
||||||
@@ -291,6 +373,8 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// B. PHYSIK
|
||||||
|
// Toleranz 1.0px
|
||||||
isGrounded := posY >= PlayerYBase-1.0
|
isGrounded := posY >= PlayerYBase-1.0
|
||||||
|
|
||||||
currentHeight := PlayerHeight
|
currentHeight := PlayerHeight
|
||||||
@@ -298,7 +382,7 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
currentHeight = PlayerHeight / 2
|
currentHeight = PlayerHeight / 2
|
||||||
if !isGrounded {
|
if !isGrounded {
|
||||||
velY += 2.0
|
velY += 2.0
|
||||||
}
|
} // Fast fall
|
||||||
}
|
}
|
||||||
|
|
||||||
if didJump && isGrounded && !isCrouching {
|
if didJump && isGrounded && !isCrouching {
|
||||||
@@ -313,21 +397,26 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
velY = 0
|
velY = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hitbox Y berechnen
|
||||||
hitboxY := posY
|
hitboxY := posY
|
||||||
if isCrouching {
|
if isCrouching {
|
||||||
hitboxY = posY + (PlayerHeight - currentHeight)
|
hitboxY = posY + (PlayerHeight - currentHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C. OBSTACLES & KOLLISION
|
||||||
nextObstacles := []ActiveObstacle{}
|
nextObstacles := []ActiveObstacle{}
|
||||||
rightmostX := 0.0
|
rightmostX := 0.0
|
||||||
|
|
||||||
for _, obs := range obstacles {
|
for _, obs := range obstacles {
|
||||||
obs.X -= GameSpeed
|
obs.X -= GameSpeed
|
||||||
|
|
||||||
|
// Ist Hindernis schon vorbei? (Hinter Spieler)
|
||||||
if obs.X+obs.Width < 50.0 {
|
if obs.X+obs.Width < 50.0 {
|
||||||
continue
|
// log.Printf("🗑️ Obstacle %s passed safely", obs.ID)
|
||||||
|
continue // Nicht in nextObstacles aufnehmen -> wird gelöscht
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kollisions-Logik
|
||||||
paddingX := 10.0
|
paddingX := 10.0
|
||||||
paddingY_Top := 25.0
|
paddingY_Top := 25.0
|
||||||
paddingY_Bottom := 5.0
|
paddingY_Bottom := 5.0
|
||||||
@@ -339,9 +428,12 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
oTop, oBottom := obs.Y+paddingY_Top, obs.Y+obs.Height-paddingY_Bottom
|
oTop, oBottom := obs.Y+paddingY_Top, obs.Y+obs.Height-paddingY_Bottom
|
||||||
|
|
||||||
if pRight > oLeft && pLeft < oRight && pBottom > oTop && pTop < oBottom {
|
if pRight > oLeft && pLeft < oRight && pBottom > oTop && pTop < oBottom {
|
||||||
|
log.Printf("💥 KOLLISION! Session: %s | Obs: %s at %.2f | PlayerY: %.2f | Crouch: %v", req.SessionID, obs.ID, obs.X, posY, isCrouching)
|
||||||
playerDead = true
|
playerDead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Behalten wenn noch relevant (sichtbar oder kurz davor)
|
||||||
|
// Wir behalten es etwas länger, damit Client Synchronisation nicht springt
|
||||||
if obs.X+obs.Width > -100 {
|
if obs.X+obs.Width > -100 {
|
||||||
nextObstacles = append(nextObstacles, obs)
|
nextObstacles = append(nextObstacles, obs)
|
||||||
if obs.X+obs.Width > rightmostX {
|
if obs.X+obs.Width > rightmostX {
|
||||||
@@ -351,6 +443,7 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
obstacles = nextObstacles
|
obstacles = nextObstacles
|
||||||
|
|
||||||
|
// D. SPAWNING
|
||||||
if rightmostX < GameWidth-10.0 {
|
if rightmostX < GameWidth-10.0 {
|
||||||
rawGap := 400.0 + rng.NextRange(0, 500)
|
rawGap := 400.0 + rng.NextRange(0, 500)
|
||||||
gap := float64(int(rawGap))
|
gap := float64(int(rawGap))
|
||||||
@@ -380,6 +473,8 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if def != nil {
|
if def != nil {
|
||||||
spawnY := GroundY - def.Height - def.YOffset
|
spawnY := GroundY - def.Height - def.YOffset
|
||||||
|
// log.Printf("✨ SPAWN: %s at %.2f (Y: %.2f)", def.ID, spawnX, spawnY)
|
||||||
|
|
||||||
obstacles = append(obstacles, ActiveObstacle{
|
obstacles = append(obstacles, ActiveObstacle{
|
||||||
ID: def.ID,
|
ID: def.ID,
|
||||||
X: spawnX,
|
X: spawnX,
|
||||||
@@ -392,9 +487,15 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if !playerDead {
|
if !playerDead {
|
||||||
score++
|
score++
|
||||||
|
} else {
|
||||||
|
// Wenn tot, brechen wir die Loop ab, um Rechenzeit zu sparen
|
||||||
|
// und senden den Dead-Status zurück
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- SPEICHERN ---
|
||||||
|
|
||||||
status := "alive"
|
status := "alive"
|
||||||
if playerDead {
|
if playerDead {
|
||||||
status = "dead"
|
status = "dead"
|
||||||
@@ -403,13 +504,18 @@ func handleValidate(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
obsJson, _ := json.Marshal(obstacles)
|
obsJson, _ := json.Marshal(obstacles)
|
||||||
|
|
||||||
rdb.HSet(ctx, key, map[string]interface{}{
|
err = rdb.HSet(ctx, key, map[string]interface{}{
|
||||||
"score": score,
|
"score": score,
|
||||||
"pos_y": fmt.Sprintf("%f", posY),
|
"pos_y": fmt.Sprintf("%f", posY),
|
||||||
"vel_y": fmt.Sprintf("%f", velY),
|
"vel_y": fmt.Sprintf("%f", velY),
|
||||||
"rng_state": rng.State,
|
"rng_state": rng.State,
|
||||||
"obstacles": string(obsJson),
|
"obstacles": string(obsJson),
|
||||||
})
|
}).Err()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ REDIS SAVE ERROR: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
rdb.Expire(ctx, key, 1*time.Hour)
|
rdb.Expire(ctx, key, 1*time.Hour)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
@@ -427,11 +533,14 @@ func handleSubmitName(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
safeName := html.EscapeString(req.Name)
|
log.Printf("📝 SUBMIT NAME: %s für Session %s", req.Name, req.SessionID)
|
||||||
|
|
||||||
|
safeName := html.EscapeString(req.Name)
|
||||||
sessionKey := "session:" + req.SessionID
|
sessionKey := "session:" + req.SessionID
|
||||||
|
|
||||||
scoreVal, err := rdb.HGet(ctx, sessionKey, "score").Result()
|
scoreVal, err := rdb.HGet(ctx, sessionKey, "score").Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("⚠️ Submit fehlgeschlagen: Session nicht gefunden %s", req.SessionID)
|
||||||
http.Error(w, "Session expired", 404)
|
http.Error(w, "Session expired", 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -547,6 +656,8 @@ func handleAdminAction(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("👮 ADMIN ACTION: %s on %s", req.Action, req.SessionID)
|
||||||
|
|
||||||
if req.Action == "approve" {
|
if req.Action == "approve" {
|
||||||
score, err := rdb.ZScore(ctx, "leaderboard:unverified", req.SessionID).Result()
|
score, err := rdb.ZScore(ctx, "leaderboard:unverified", req.SessionID).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -575,29 +686,21 @@ func handleClaimDelete(w http.ResponseWriter, r *http.Request) {
|
|||||||
realCode, err := rdb.HGet(ctx, sessionKey, "claim_code").Result()
|
realCode, err := rdb.HGet(ctx, sessionKey, "claim_code").Result()
|
||||||
|
|
||||||
if err != nil || realCode == "" {
|
if err != nil || realCode == "" {
|
||||||
|
log.Printf("⚠️ Claim Delete Failed: Session/Code missing %s", req.SessionID)
|
||||||
http.Error(w, "Not found", 404)
|
http.Error(w, "Not found", 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if realCode != req.ClaimCode {
|
if realCode != req.ClaimCode {
|
||||||
|
log.Printf("⛔ Claim Delete Denied: Wrong Code %s vs %s", req.ClaimCode, realCode)
|
||||||
http.Error(w, "Wrong Code", 403)
|
http.Error(w, "Wrong Code", 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("🗑️ USER DELETE: Session %s deleted via code", req.SessionID)
|
||||||
rdb.ZRem(ctx, "leaderboard:unverified", req.SessionID)
|
rdb.ZRem(ctx, "leaderboard:unverified", req.SessionID)
|
||||||
rdb.ZRem(ctx, "leaderboard:public", req.SessionID)
|
rdb.ZRem(ctx, "leaderboard:public", req.SessionID)
|
||||||
rdb.HDel(ctx, sessionKey, "name")
|
rdb.HDel(ctx, sessionKey, "name")
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOr(s string, def float64) float64 {
|
|
||||||
if s == "" {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
v, err := strconv.ParseFloat(s, 64)
|
|
||||||
if err != nil {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user