package main import ( "context" "errors" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "github.com/ruby-sea-battle/game-server/internal/config" "github.com/ruby-sea-battle/game-server/internal/game" "github.com/ruby-sea-battle/game-server/internal/ws" _ "github.com/joho/godotenv/autoload" ) func main() { log := slog.New(slog.NewTextHandler(os.Stdout, nil)) cfg, err := config.Load() if err != nil { log.Error("configuration error", "err", err) os.Exit(1) } manager := game.NewManager(cfg.JoinTimeout) handler := ws.NewHandler(manager, cfg.JWTSecret, cfg.PublicAddr, cfg.AllowedOrigins, log) mux := http.NewServeMux() mux.Handle("/ws", handler) mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) }) srv := &http.Server{ Addr: cfg.ListenAddr, Handler: mux, ReadHeaderTimeout: 10 * time.Second, } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() go func() { log.Info("game server listening", "listen_addr", cfg.ListenAddr, "public_addr", cfg.PublicAddr, "join_timeout", cfg.JoinTimeout, ) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Error("server failed", "err", err) os.Exit(1) } }() <-ctx.Done() log.Info("shutting down") manager.Shutdown() shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = srv.Shutdown(shutdownCtx) }