mchess-server/main.go

189 lines
4.7 KiB
Go

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)
}