package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "net/http" "sync" "sync/atomic" "time" ) // Konfiguration (über Flags steuerbar) var ( targetURL string numClients int duration time.Duration chunkTicks int ) // Statistiken (Atomar für Thread-Sicherheit) var ( totalRequests int64 totalErrors int64 totalLatency int64 // in Millisekunden ) // Datenstrukturen für Kommunikation (Minimal) type StartResponse struct { SessionID string `json:"sessionId"` } type Input struct { Tick int `json:"t"` Act string `json:"act"` } type ValidateRequest struct { SessionID string `json:"sessionId"` Inputs []Input `json:"inputs"` TotalTicks int `json:"totalTicks"` } func main() { // 1. Argumente parsen flag.StringVar(&targetURL, "url", "http://localhost:8080", "URL zum Server") flag.IntVar(&numClients, "clients", 50, "Anzahl gleichzeitiger Spieler") flag.DurationVar(&duration, "time", 10*time.Second, "Dauer des Tests (z.B. 10s, 1m)") flag.IntVar(&chunkTicks, "ticks", 60, "Simulierte Ticks pro Anfrage (Last)") flag.Parse() fmt.Printf("🚀 Starte Benchmark auf %s\n", targetURL) fmt.Printf("👥 Spieler (Clients): %d\n", numClients) fmt.Printf("⏱️ Dauer: %s\n", duration) fmt.Printf("🏋️ Last pro Request: %d Ticks Physik\n", chunkTicks) fmt.Println("---------------------------------------------------") // 2. Clients starten var wg sync.WaitGroup start := time.Now() // Kanal um den Workern das Stop-Signal zu geben stopChan := make(chan struct{}) // Timer für das Testende time.AfterFunc(duration, func() { close(stopChan) }) for i := 0; i < numClients; i++ { wg.Add(1) go runClient(i, &wg, stopChan) } // Warten bis alle fertig sind wg.Wait() totalTime := time.Since(start) // 3. Auswertung printReport(totalTime) } func runClient(id int, wg *sync.WaitGroup, stopChan <-chan struct{}) { defer wg.Done() // A. Session Starten sessionID, err := startSession() if err != nil { log.Printf("[Client %d] Start Fehler: %v", id, err) atomic.AddInt64(&totalErrors, 1) return } // B. Game Loop simulieren client := &http.Client{Timeout: 5 * time.Second} for { select { case <-stopChan: return // Testzeit abgelaufen default: // Wir simulieren einen Chunk (z.B. Spieler rennt einfach geradeaus) reqData := ValidateRequest{ SessionID: sessionID, Inputs: []Input{}, // Keine Eingaben = Nur Rennen TotalTicks: chunkTicks, } payload, _ := json.Marshal(reqData) reqStart := time.Now() resp, err := client.Post(targetURL+"/api/validate", "application/json", bytes.NewBuffer(payload)) latency := time.Since(reqStart).Milliseconds() if err != nil || resp.StatusCode != 200 { // Fehler zählen atomic.AddInt64(&totalErrors, 1) if resp != nil { io.Copy(io.Discard, resp.Body) // Body leeren resp.Body.Close() } } else { // Erfolg zählen atomic.AddInt64(&totalRequests, 1) atomic.AddInt64(&totalLatency, latency) io.Copy(io.Discard, resp.Body) resp.Body.Close() } // Optional: Kurz warten um "echte" 60 FPS zu simulieren? // Für Stresstests lassen wir das weg, wir wollen sehen was MAXIMAL geht. // time.Sleep(100 * time.Millisecond) } } } func startSession() (string, error) { resp, err := http.Post(targetURL+"/api/start", "application/json", nil) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != 200 { return "", fmt.Errorf("status %d", resp.StatusCode) } var data StartResponse if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return "", err } return data.SessionID, nil } func printReport(totalTime time.Duration) { reqs := atomic.LoadInt64(&totalRequests) errs := atomic.LoadInt64(&totalErrors) lat := atomic.LoadInt64(&totalLatency) avgLat := 0.0 if reqs > 0 { avgLat = float64(lat) / float64(reqs) } rps := float64(reqs) / totalTime.Seconds() fmt.Println("\n📊 ERGEBNISSE") fmt.Println("---------------------------------------------------") fmt.Printf("✅ Erfolgreiche Requests: %d\n", reqs) fmt.Printf("❌ Fehler: %d\n", errs) fmt.Printf("⚡ Requests pro Sekunde: %.2f Req/s\n", rps) fmt.Printf("🐢 Ø Antwortzeit: %.2f ms\n", avgLat) fmt.Println("---------------------------------------------------") if avgLat > 100 { fmt.Println("⚠️ WARNUNG: Server antwortet langsam (>100ms). Spieler könnten Lags spüren.") } else { fmt.Println("✨ PERFEKT: Server ist schnell genug für flüssiges Spielen!") } }