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
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 2m28s
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user