Marco
ff2ec599fe
In order to simplify special moves like en passant or castling for the client, we want to deliver the board state after every move (and not only start square and end square). With PGN we can encode a chess position into a string. This commit implies changes to logic of the pieces' shortnames. This will break the client/server connection (at least for promotions).
161 lines
4.0 KiB
Go
161 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"mchess_server/api"
|
|
"mchess_server/chess"
|
|
lobbies "mchess_server/lobby_registry"
|
|
"mchess_server/usher"
|
|
"mchess_server/utils"
|
|
"net/http"
|
|
"os"
|
|
"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() {
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
router := gin.Default()
|
|
|
|
router.GET("/api/random", registerForRandomGame)
|
|
router.GET("/api/hostPrivate", hostPrivateGame)
|
|
router.POST("/api/joinPrivate", joinPrivateGame)
|
|
router.GET("/api/ws", registerWebSocketConnection)
|
|
|
|
if hostname == "mbook" {
|
|
log.Println("Starting service WITHOUT TLS")
|
|
log.Fatal(router.Run("localhost: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()
|
|
lobby := usher.WelcomeNewPlayer(player)
|
|
usher.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
|
|
mut.Unlock()
|
|
|
|
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()
|
|
lobby := u.CreateNewPrivateLobby(player)
|
|
u.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
|
|
mut.Unlock()
|
|
|
|
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("OI, WHERES ME LOGS")
|
|
log.Println(c.Request.Body)
|
|
err := c.ShouldBindJSON(&req)
|
|
if err != nil || req.Passphrase == nil || *req.Passphrase == "" {
|
|
c.IndentedJSON(http.StatusNotFound, req)
|
|
}
|
|
|
|
player := chess.NewPlayer(uuid.New())
|
|
u := usher.GetUsher()
|
|
|
|
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: ", 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)
|
|
player, found := lobby.GetPlayerByUUID(*info.PlayerID)
|
|
if !found {
|
|
conn.Close(websocket.StatusCode(400), "player not found")
|
|
return
|
|
}
|
|
if player.Conn != nil {
|
|
player.Conn.Close(websocket.StatusCode(400), "closing existing connection")
|
|
}
|
|
player.SetConnection(ctx, conn)
|
|
log.Println("player after setting connection: ", player)
|
|
}
|