Private
Public Access
1
0
Files
EscapeFromTeacher/cmd/server/main.go

185 lines
4.9 KiB
Go

package main
import (
"encoding/json"
"io/ioutil"
"log"
"path/filepath"
"sync"
"github.com/nats-io/nats.go"
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/server"
)
// Globaler Zustand des Servers
var (
rooms = make(map[string]*server.Room)
playerSessions = make(map[string]*server.Room)
mu sync.RWMutex
globalWorld *game.World
)
func main() {
log.Println("🚀 Escape From Teacher SERVER startet...")
// 1. WELT & ASSETS LADEN
globalWorld = game.NewWorld()
loadServerAssets(globalWorld)
// 1b. Redis-Leaderboard initialisieren
if err := server.InitLeaderboard("localhost:6379"); err != nil {
log.Fatal("❌ Konnte nicht zu Redis verbinden: ", err)
}
// 2. NATS VERBINDUNG
natsURL := "nats://localhost:4222"
nc, err := nats.Connect(natsURL)
if err != nil {
log.Fatal("❌ Konnte nicht zu NATS verbinden: ", err)
}
defer nc.Close()
ec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
if err != nil {
log.Fatal("❌ JSON Encoder Fehler: ", err)
}
log.Println("✅ Verbunden mit NATS unter", natsURL)
// 3. HANDLER: GAME JOIN
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
if playerID == "" {
playerID = "Unknown"
}
roomID := req.RoomID
if roomID == "" {
roomID = "lobby"
}
mu.Lock()
defer mu.Unlock()
// Raum finden oder erstellen
room, exists := rooms[roomID]
if !exists {
log.Printf("🆕 Erstelle neuen Raum: '%s'", roomID)
room = server.NewRoom(roomID, nc, globalWorld)
rooms[roomID] = room
// Starte den Game-Loop (Physik)
go room.RunLoop()
}
// Spieler hinzufügen (ID, Name)
room.AddPlayer(playerID, req.Name)
// Session speichern
playerSessions[playerID] = room
log.Printf("➡️ Spieler '%s' ist Raum '%s' beigetreten.", playerID, roomID)
})
if err != nil {
log.Fatal("❌ Fehler beim Subscribe auf game.join:", err)
}
log.Printf("👂 Lausche auf 'game.join'... (Sub Valid: %v)", sub.IsValid())
// TEST: Auch mit Raw-NATS lauschen
nc.Subscribe("game.join", func(m *nats.Msg) {
log.Printf("🔍 RAW NATS: Nachricht empfangen auf game.join: %s", string(m.Data))
})
// 4. HANDLER: INPUT
_, _ = ec.Subscribe("game.input", func(input *game.ClientInput) {
mu.RLock()
room, ok := playerSessions[input.PlayerID]
mu.RUnlock()
if ok {
room.HandleInput(*input)
}
})
// 5. HANDLER: SCORE SUBMISSION
_, _ = ec.Subscribe("score.submit", func(submission *game.ScoreSubmission) {
log.Printf("📊 Score-Submission: %s (%s) mit %d Punkten", submission.PlayerName, submission.PlayerCode, submission.Score)
added := server.GlobalLeaderboard.AddScore(submission.PlayerName, submission.PlayerCode, submission.Score)
if added {
log.Printf("✅ Score akzeptiert für %s", submission.PlayerName)
}
})
// 6. HANDLER: LEADERBOARD REQUEST (alt, für Kompatibilität)
_, _ = ec.Subscribe("leaderboard.get", func(subject, reply string, _ *struct{}) {
top10 := server.GlobalLeaderboard.GetTop10()
log.Printf("📊 Leaderboard-Request beantwortet: %d Einträge", len(top10))
ec.Publish(reply, top10)
})
// 7. HANDLER: LEADERBOARD REQUEST (neu, für WebSocket-Gateway)
_, _ = ec.Subscribe("leaderboard.request", func(req *game.LeaderboardRequest) {
top10 := server.GlobalLeaderboard.GetTop10()
log.Printf("📊 Leaderboard-Request (Mode=%s): %d Einträge", req.Mode, len(top10))
// Response an den angegebenen Channel senden
if req.ResponseChannel != "" {
resp := game.LeaderboardResponse{
Entries: top10,
}
ec.Publish(req.ResponseChannel, &resp)
log.Printf("📤 Leaderboard-Response gesendet an %s", req.ResponseChannel)
}
})
log.Println("✅ Server bereit. Warte auf Spieler...")
// 5. WEBSOCKET-GATEWAY STARTEN (für Browser-Clients)
go StartWebSocketGateway("8080", ec)
// Block forever
select {}
}
func loadServerAssets(w *game.World) {
assetDir := "./cmd/client/assets"
chunkDir := filepath.Join(assetDir, "chunks")
// Manifest laden
manifestPath := filepath.Join(assetDir, "assets.json")
data, err := ioutil.ReadFile(manifestPath)
if err == nil {
var m game.AssetManifest
json.Unmarshal(data, &m)
w.Manifest = m
log.Printf("📦 Manifest geladen: %d Assets", len(m.Assets))
} else {
log.Println("⚠️ Manifest nicht gefunden:", manifestPath)
}
// Chunks laden
files, err := ioutil.ReadDir(chunkDir)
if err == nil {
for _, f := range files {
if filepath.Ext(f.Name()) == ".json" {
fullPath := filepath.Join(chunkDir, f.Name())
cData, err := ioutil.ReadFile(fullPath)
if err == nil {
var chunk game.Chunk
json.Unmarshal(cData, &chunk)
if chunk.ID == "" {
chunk.ID = f.Name()[0 : len(f.Name())-5]
}
w.ChunkLibrary[chunk.ID] = chunk
log.Printf("🧩 Chunk geladen: %s", chunk.ID)
}
}
}
} else {
log.Println("⚠️ Chunk Ordner nicht gefunden:", chunkDir)
}
}