Files
tiennm99 4c56a23e58 feat: add Go game server with matchmaking, lobby, and WebSocket support
Implement a real-time game server using protobuf over WebSocket:
- Protobuf schema with 14 message types in oneof Envelope
- Room-based lobby with join/leave/chat broadcast
- ELO-based matchmaking with expanding range (±100 to ±500)
- Turn-based game session lifecycle management
- Multi-stage Docker build (Alpine)
- Unit tests for matchmaking and game session logic
2026-04-04 21:25:03 +07:00

62 lines
1.4 KiB
Go

package main
import (
"log"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/tiennm99/260404/internal/game"
"github.com/tiennm99/260404/internal/lobby"
"github.com/tiennm99/260404/internal/matchmaking"
"github.com/tiennm99/260404/internal/player"
"github.com/tiennm99/260404/internal/ws"
)
func main() {
addr := os.Getenv("ADDR")
if addr == "" {
addr = ":8080"
}
lobbyMgr := lobby.NewManager()
gameMgr := game.NewManager()
// Matchmaker callback: create a game session when players are matched.
onMatch := func(players []*player.Player, sessionID string) {
s := gameMgr.Create(sessionID, players)
s.BroadcastState()
log.Printf("match created: session=%s players=%d", sessionID, len(players))
}
mm := matchmaking.New(onMatch)
mm.Start()
defer mm.Stop()
wsHandler := ws.NewHandler(lobbyMgr, mm, gameMgr)
mux := http.NewServeMux()
mux.Handle("/ws", wsHandler)
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})
srv := &http.Server{Addr: addr, Handler: mux}
// Graceful shutdown.
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("shutting down...")
_ = srv.Close()
}()
log.Printf("game server listening on %s", addr)
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}