From 6606d67a2171852726e4def135a8261b93bc370f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Untersch=C3=BCtz?= Date: Sun, 4 Jan 2026 18:28:25 +0100 Subject: [PATCH] Switch `game.join` and leaderboard handlers (`leaderboard.get`, `leaderboard.request`) to NATS Queue Groups, remove room-to-pod assignment logic for simplified load balancing, and add detailed Pod-specific logging. --- cmd/server/main.go | 62 ++++++++-------------------------------------- 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 1a41acd..0bbd070 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -111,8 +111,8 @@ func main() { } log.Printf("✅ Verbunden mit NATS: %s", natsURL) - // 3. HANDLER: GAME JOIN (broadcast - alle Pods hören, aber nur zuständiger erstellt) - sub, err := ec.Subscribe("game.join", func(req *game.JoinRequest) { + // 3. HANDLER: GAME JOIN (Queue Group - nur EIN Pod bekommt den JOIN) + sub, err := ec.QueueSubscribe("game.join", "room-handlers", func(req *game.JoinRequest) { playerID := req.Name if playerID == "" { playerID = "Unknown" @@ -123,13 +123,7 @@ func main() { roomID = "lobby" } - log.Printf("📥 JOIN empfangen: Name=%s, RoomID=%s", req.Name, roomID) - - // Prüfe ob dieser Pod für den Raum zuständig ist - if !isResponsibleForRoom(roomID) { - log.Printf("⏭️ Überspringe JOIN - nicht zuständig für Raum '%s'", roomID) - return - } + log.Printf("📥 JOIN empfangen: Name=%s, RoomID=%s (Pod: %s)", req.Name, roomID, serverID) mu.Lock() defer mu.Unlock() @@ -137,7 +131,7 @@ func main() { // Raum finden oder erstellen room, exists := rooms[roomID] if !exists { - log.Printf("🆕 Erstelle neuen Raum: '%s' (zuständiger Pod)", roomID) + log.Printf("🆕 Erstelle neuen Raum: '%s' auf Pod %s", roomID, serverID) room = server.NewRoom(roomID, nc, globalWorld) rooms[roomID] = room @@ -158,7 +152,7 @@ func main() { // Session speichern playerSessions[playerID] = room - log.Printf("➡️ Spieler '%s' ist Raum '%s' beigetreten.", playerID, roomID) + log.Printf("✅ Spieler '%s' ist Raum '%s' beigetreten (Pod: %s).", playerID, roomID, serverID) }) if err != nil { @@ -229,17 +223,17 @@ func main() { } }) - // 7. HANDLER: LEADERBOARD REQUEST (alt, für Kompatibilität) - _, _ = ec.Subscribe("leaderboard.get", func(subject, reply string, _ *struct{}) { + // 7. HANDLER: LEADERBOARD REQUEST (alt, für Kompatibilität) - Queue Group + _, _ = ec.QueueSubscribe("leaderboard.get", "leaderboard-handlers", func(subject, reply string, _ *struct{}) { top10 := server.GlobalLeaderboard.GetTop10() - log.Printf("📊 Leaderboard-Request beantwortet: %d Einträge", len(top10)) + log.Printf("📊 Leaderboard-Request beantwortet: %d Einträge (Pod: %s)", len(top10), serverID) ec.Publish(reply, top10) }) - // 8. HANDLER: LEADERBOARD REQUEST (neu, für WebSocket-Gateway) - _, _ = ec.Subscribe("leaderboard.request", func(req *game.LeaderboardRequest) { + // 8. HANDLER: LEADERBOARD REQUEST (neu, für WebSocket-Gateway) - Queue Group + _, _ = ec.QueueSubscribe("leaderboard.request", "leaderboard-handlers", func(req *game.LeaderboardRequest) { top10 := server.GlobalLeaderboard.GetTop10() - log.Printf("📊 Leaderboard-Request (Mode=%s): %d Einträge", req.Mode, len(top10)) + log.Printf("📊 Leaderboard-Request (Mode=%s): %d Einträge (Pod: %s)", req.Mode, len(top10), serverID) // Response an den angegebenen Channel senden if req.ResponseChannel != "" { @@ -267,40 +261,6 @@ func getEnv(key, defaultValue string) string { return defaultValue } -// isResponsibleForRoom prüft ob dieser Pod für den Raum zuständig ist -func isResponsibleForRoom(roomID string) bool { - // Wenn nur 1 Replica läuft, sind wir immer zuständig - totalReplicas := getEnv("TOTAL_REPLICAS", "1") - if totalReplicas == "1" { - return true - } - - if len(roomID) == 0 { - return true - } - - // Hash-basierte Zuweisung: RoomID → Pod - // Verwende einfachen Hash für bessere Verteilung - hash := 0 - for _, c := range roomID { - hash = (hash*31 + int(c)) % 2 - } - - // Bestimme Pod-Nummer aus ServerID - podNumber := 0 - if len(serverID) > 0 { - lastChar := serverID[len(serverID)-1] - if lastChar >= '0' && lastChar <= '9' { - podNumber = int(lastChar - '0') - } - } - - responsible := (hash % 2) == (podNumber % 2) - log.Printf("🎯 Room '%s' → Hash=%d, Pod=%d (%s) → Responsible=%v", roomID, hash, podNumber, serverID, responsible) - - return responsible -} - func loadServerAssets(w *game.World) { assetDir := "./cmd/client/web/assets" chunkDir := filepath.Join(assetDir, "chunks")