Private
Public Access
1
0

Introduce room assignment logic based on POD_NAME and TOTAL_REPLICAS, enable selective handling for game.join and score.submit events, and update environment configurations.
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m28s

This commit is contained in:
Sebastian Unterschütz
2026-01-04 17:16:09 +01:00
parent a17a6f5e9f
commit 8f49a691f7
2 changed files with 68 additions and 7 deletions

View File

@@ -21,11 +21,22 @@ var (
playerSessions = make(map[string]*server.Room) playerSessions = make(map[string]*server.Room)
mu sync.RWMutex mu sync.RWMutex
globalWorld *game.World globalWorld *game.World
serverID string // Eindeutige Server-ID für diesen Pod
) )
func main() { func main() {
log.Println("🚀 Escape From Teacher SERVER startet...") log.Println("🚀 Escape From Teacher SERVER startet...")
// 0. Server-ID generieren (Hostname oder zufällig)
serverID = os.Getenv("HOSTNAME")
if serverID == "" {
serverID = os.Getenv("POD_NAME")
}
if serverID == "" {
serverID = "server-" + time.Now().Format("150405")
}
log.Printf("🆔 Server-ID: %s", serverID)
// 1. WELT & ASSETS LADEN // 1. WELT & ASSETS LADEN
globalWorld = game.NewWorld() globalWorld = game.NewWorld()
loadServerAssets(globalWorld) loadServerAssets(globalWorld)
@@ -100,10 +111,8 @@ func main() {
} }
log.Printf("✅ Verbunden mit NATS: %s", natsURL) log.Printf("✅ Verbunden mit NATS: %s", natsURL)
// 3. HANDLER: GAME JOIN (mit Queue Group für Load Balancing) // 3. HANDLER: GAME JOIN (broadcast - alle Pods hören, aber nur zuständiger erstellt)
sub, err := ec.QueueSubscribe("game.join", "game-servers", func(req *game.JoinRequest) { sub, err := ec.Subscribe("game.join", func(req *game.JoinRequest) {
log.Printf("📥 JOIN empfangen: Name=%s, RoomID=%s", req.Name, req.RoomID)
playerID := req.Name playerID := req.Name
if playerID == "" { if playerID == "" {
playerID = "Unknown" playerID = "Unknown"
@@ -114,13 +123,21 @@ func main() {
roomID = "lobby" 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
}
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
// Raum finden oder erstellen // Raum finden oder erstellen
room, exists := rooms[roomID] room, exists := rooms[roomID]
if !exists { if !exists {
log.Printf("🆕 Erstelle neuen Raum: '%s'", roomID) log.Printf("🆕 Erstelle neuen Raum: '%s' (zuständiger Pod)", roomID)
room = server.NewRoom(roomID, nc, globalWorld) room = server.NewRoom(roomID, nc, globalWorld)
rooms[roomID] = room rooms[roomID] = room
@@ -170,8 +187,8 @@ func main() {
} }
}) })
// 6. HANDLER: SCORE SUBMISSION // 6. HANDLER: SCORE SUBMISSION (Queue Group - nur ein Pod speichert)
_, _ = ec.Subscribe("score.submit", func(submission *game.ScoreSubmission) { _, _ = ec.QueueSubscribe("score.submit", "score-handlers", func(submission *game.ScoreSubmission) {
// Verwende Team-Name wenn vorhanden (Coop-Mode), sonst Player-Name (Solo-Mode) // Verwende Team-Name wenn vorhanden (Coop-Mode), sonst Player-Name (Solo-Mode)
displayName := submission.PlayerName displayName := submission.PlayerName
if submission.TeamName != "" { if submission.TeamName != "" {
@@ -234,6 +251,44 @@ func getEnv(key, defaultValue string) string {
return defaultValue 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
}
// Hash-basierte Zuweisung: RoomID → Pod
// Einfacher Ansatz: erster Buchstabe von RoomID
// A-M → Pod 0, N-Z → Pod 1
if len(roomID) == 0 {
return true
}
firstChar := roomID[0]
// Für 2 Replicas: A-M und N-Z splitten
if totalReplicas == "2" {
// Pod Namen sind meist: escape-game-0, escape-game-1, etc.
if firstChar <= 'M' || firstChar <= 'm' {
return serverID[len(serverID)-1] == '0' || !hasDigitSuffix()
}
return serverID[len(serverID)-1] == '1' || hasDigitSuffix()
}
// Fallback: immer zuständig
return true
}
func hasDigitSuffix() bool {
if len(serverID) == 0 {
return false
}
lastChar := serverID[len(serverID)-1]
return lastChar >= '0' && lastChar <= '9'
}
func loadServerAssets(w *game.World) { func loadServerAssets(w *game.World) {
assetDir := "./cmd/client/web/assets" assetDir := "./cmd/client/web/assets"
chunkDir := filepath.Join(assetDir, "chunks") chunkDir := filepath.Join(assetDir, "chunks")

View File

@@ -27,6 +27,12 @@ spec:
value: "redis:6379" value: "redis:6379"
- name: NATS_URL - name: NATS_URL
value: "nats://nats:4222" value: "nats://nats:4222"
- name: TOTAL_REPLICAS
value: "2"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /health path: /health