package main import ( "context" "encoding/json" "flag" "fmt" "log" "mchess_server/api" "mchess_server/chess" lobbies "mchess_server/lobby_registry" "mchess_server/usher" "mchess_server/utils" "net/http" "sync" "github.com/gin-gonic/gin" "github.com/google/uuid" "nhooyr.io/websocket" ) var cert_path = "/etc/letsencrypt/live/chess.sw-gross.de/" var cert_file = cert_path + "fullchain.pem" var key_file = cert_path + "privkey.pem" var mut sync.Mutex func main() { var debugMode bool debugModeLong := flag.Bool("debug", false, "activates debug mode") debugModeShort := flag.Bool("d", false, "activates debug mode") flag.Parse() if *debugModeShort || *debugModeLong { debugMode = true } router := gin.Default() router.GET("/api/random", registerForRandomGame) router.GET("/api/hostPrivate", hostPrivateGame) router.POST("/api/joinPrivate", joinPrivateGame) router.GET("/api/ws", registerWebSocketConnection) if debugMode { log.Println("Starting service WITHOUT TLS") log.Fatal(router.Run(":8080")) } else { gin.SetMode(gin.ReleaseMode) log.Println("Starting in release mode") log.Println("Starting service with TLS") log.Fatal(router.RunTLS("chess.sw-gross.de:9999", cert_file, key_file)) } } func registerForRandomGame(c *gin.Context) { player := chess.NewPlayer(uuid.New()) usher := usher.GetUsher() mut.Lock() defer mut.Unlock() lobby := usher.WelcomeNewPlayer(player) usher.AddPlayerToLobbyAndStartGameIfFull(player, lobby) info := api.PlayerInfo{ PlayerID: &player.Uuid, LobbyID: &lobby.Uuid, } log.Println("responding with info ", info) c.Header("Access-Control-Allow-Origin", "*") c.IndentedJSON(http.StatusOK, info) } func hostPrivateGame(c *gin.Context) { player := chess.NewPlayer(uuid.New()) u := usher.GetUsher() mut.Lock() defer mut.Unlock() lobby := u.CreateNewPrivateLobby(player) u.AddPlayerToLobbyAndStartGameIfFull(player, lobby) passphrase := lobby.Passphrase.String() info := api.PlayerInfo{ PlayerID: &player.Uuid, LobbyID: &lobby.Uuid, Passphrase: &passphrase, } c.Header("Access-Control-Allow-Origin", "*") c.IndentedJSON(http.StatusOK, info) } func joinPrivateGame(c *gin.Context) { req := api.PlayerInfo{} log.Println(c.Request.Body) err := c.ShouldBindJSON(&req) if err != nil || req.Passphrase == nil || *req.Passphrase == "" { c.IndentedJSON(http.StatusNotFound, req) } u := usher.GetUsher() if req.Passphrase != nil && *req.Passphrase != "" && req.PlayerID != nil && req.LobbyID != nil { //is reconnect lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase)) _, found := lobby.GetPlayerByUUID(*req.PlayerID) if found { c.IndentedJSON( http.StatusOK, api.PlayerInfo{ PlayerID: req.PlayerID, LobbyID: req.LobbyID, Passphrase: req.Passphrase, }) return } else { c.IndentedJSON(http.StatusNotFound, req) } } player := chess.NewPlayer(uuid.New()) mut.Lock() defer mut.Unlock() lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase)) if lobby != nil { u.AddPlayerToLobbyAndStartGameIfFull(player, lobby) } else { c.IndentedJSON(http.StatusNotFound, req) return } info := api.PlayerInfo{ PlayerID: &player.Uuid, LobbyID: &lobby.Uuid, Passphrase: req.Passphrase, } c.Header("Access-Control-Allow-Origin", "*") c.IndentedJSON(http.StatusOK, info) } func registerWebSocketConnection(c *gin.Context) { webSocketConn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{OriginPatterns: []string{"chess.sw-gross.de", "localhost:*"}}) if err != nil { log.Println(err) return } go waitForAndHandlePlayerID(c, webSocketConn) } func waitForAndHandlePlayerID(ctx context.Context, conn *websocket.Conn) { msgType, msg, err := conn.Read(ctx) if err != nil { errorMessage := fmt.Sprintf("Reading from websocket connection did not work: %s", err) log.Println(errorMessage) conn.Close(websocket.StatusCode(400), errorMessage) return } log.Println("read from websocket endpoint: ", msgType, string(msg), err) var info api.PlayerInfo err = json.Unmarshal(msg, &info) if err != nil { errorMessage := fmt.Sprintf("Unmarshaling message did not work: %s", err) log.Println(errorMessage) conn.Close(websocket.StatusCode(400), errorMessage) return } lobby := lobbies.GetLobbyRegistry().GetLobbyByUUID(*info.LobbyID) if lobby == nil { conn.Close(websocket.StatusCode(400), "lobby not found") return } player, found := lobby.GetPlayerByUUID(*info.PlayerID) if !found { conn.Close(websocket.StatusCode(400), "player not found") return } if player.Conn.HasWebsocketConnection() { player.Conn.Close("closing existing connection") } lobby.Game.SetWebsocketConnectionFor(ctx, player, conn) log.Println("player after setting connection: ", player) }