diff --git a/chess/game.go b/chess/game.go index d2bdb79..df94680 100644 --- a/chess/game.go +++ b/chess/game.go @@ -2,9 +2,9 @@ package chess import ( "log" + "math/rand" "mchess_server/api" "mchess_server/types" - "time" "github.com/google/uuid" ) @@ -29,7 +29,7 @@ func NewGame() *Game { var game = Game{ id: uuid.New(), board: newBoard(), - gameState: PlayerToMove, + gameState: Init, } game.board.Init() @@ -44,15 +44,18 @@ func (game Game) GetPlayer1() *Player { return game.players[0] } +func (game Game) GetRandomPlayer() *Player { + index := rand.Int() % 2 + return game.players[index] +} + func (game Game) GetPlayer2() *Player { return game.players[1] } func (game *Game) prepare() { - ok := game.waitForWebsocketConnections() - if !ok { - return - } + game.players[0].color = types.White + game.players[1].color = types.Black err := game.notifyPlayersAboutGameStart() if err != nil { @@ -60,17 +63,11 @@ func (game *Game) prepare() { } } -//CHANGES: -/* -Do not wait for the players websocket connection before the game. -Let Connection do it. -Then here in game handler, just read from Connection (aka messagebuffer) and when data arrives, we read it -Question: how do we handle connection losses, should game handler observe state of connection and send a notification to the other player when one player is disconnected -Question: How would reconnect work in this scenario? -*/ func (game *Game) Handle() { defer game.killGame() + game.prepare() + var receivedMove types.Move var err error @@ -78,10 +75,14 @@ func (game *Game) Handle() { switch game.gameState { case Init: game.currentTurnPlayer = game.GetPlayer1() + game.gameState = Prepare + case Prepare: game.prepare() + game.gameState = PlayerToMove + case PlayerToMove: - log.Println("with player ", game.currentTurnPlayer, " to move") + log.Println("with ", game.currentTurnPlayer.GetPlayerColor(), " to move") receivedMove, err = game.currentTurnPlayer.ReadMove() if err != nil { log.Println("Error while reading message:", err) @@ -131,25 +132,6 @@ func (game *Game) killGame() { log.Println("Game should be killed") } -func (game *Game) waitForWebsocketConnections() bool { - timer := time.NewTimer(5 * time.Second) - numberOfConnections := 0 - waitingForPlayers := make(chan bool) - - go game.GetPlayer1().WaitForWebsocketConnection(waitingForPlayers) - go game.GetPlayer2().WaitForWebsocketConnection(waitingForPlayers) - - for numberOfConnections < 2 { - select { - case <-waitingForPlayers: - numberOfConnections++ - case <-timer.C: - return false - } - } - return true -} - func (game Game) notifyPlayersAboutGameStart() error { colorDeterminedPlayer1, err := api.GetColorDeterminedMessage(types.White) if err != nil { diff --git a/chess/player.go b/chess/player.go index 2ebc183..353da54 100644 --- a/chess/player.go +++ b/chess/player.go @@ -8,34 +8,32 @@ import ( "mchess_server/api" conn "mchess_server/connection" "mchess_server/types" - "time" "github.com/google/uuid" "nhooyr.io/websocket" ) type Player struct { - Uuid uuid.UUID - Conn *conn.Connection - InGame bool - wsConnEstablished chan bool - context context.Context + Uuid uuid.UUID + Conn *conn.Connection + InGame bool + color types.ChessColor } func NewPlayer(uuid uuid.UUID) *Player { return &Player{ - Uuid: uuid, - Conn: nil, - InGame: false, - wsConnEstablished: make(chan bool), - context: context.Background(), + Uuid: uuid, + Conn: conn.NewConnection(conn.WithContext(context.Background())), + InGame: false, } } -func (p *Player) SetConnection(ctx context.Context, ws *websocket.Conn) { - p.Conn = conn.NewConnection(conn.WithWebsocket(ws), conn.WithContext(p.context)) - p.context = ctx - p.wsConnEstablished <- true +func (p Player) HasWebsocketConnection() bool { + return p.Conn.HasWebsocketConnection() +} + +func (p *Player) SetWebsocketConnection(ctx context.Context, ws *websocket.Conn) { + p.Conn.SetWebsocketConnection(ws) } func (p *Player) SendMoveAndPosition(move types.Move, boardPosition string) error { @@ -58,9 +56,7 @@ func (p *Player) SendMoveAndPosition(move types.Move, boardPosition string) erro } func (p *Player) writeMessage(msg []byte) error { - log.Printf("Writing message: %s to player %s", string(msg), p.Uuid.String()) - - return p.Conn.Write(p.context, msg) + return p.Conn.Write(msg) } func (p *Player) ReadMove() (types.Move, error) { @@ -83,19 +79,12 @@ func (p *Player) ReadMove() (types.Move, error) { } func (p *Player) readMessage() ([]byte, error) { - msg, err := p.Conn.Read(p.context) + msg, err := p.Conn.Read() log.Printf("Reading message: %s from player %s", string(msg), p.Uuid.String()) return msg, err } -func (p *Player) WaitForWebsocketConnection(c chan bool) { - timer := time.NewTimer(5 * time.Second) - - select { - case <-p.wsConnEstablished: - c <- true - case <-timer.C: - return - } +func (p Player) GetPlayerColor() string { + return string(p.color) } diff --git a/connection/type.go b/connection/type.go index 5b2d31c..b61ae3a 100644 --- a/connection/type.go +++ b/connection/type.go @@ -2,34 +2,28 @@ package connection import ( "context" + "log" "nhooyr.io/websocket" ) type Connection struct { - ws *websocket.Conn - ctx context.Context - buffer MessageBuffer + ws *websocket.Conn + wsConnectionEstablished chan bool + ctx context.Context + buffer MessageBuffer } func NewConnection(options ...func(*Connection)) *Connection { connection := Connection{ - buffer: *newMessageBuffer(100), + buffer: *newMessageBuffer(100), + wsConnectionEstablished: make(chan bool), } for _, option := range options { option(&connection) } - if connection.ws != nil { - go func() { - for { - _, msg, _ := connection.ws.Read(connection.ctx) - connection.buffer.Insert(string(msg)) - } - }() - } - return &connection } @@ -45,13 +39,43 @@ func WithContext(ctx context.Context) func(*Connection) { } } -func (conn *Connection) Write(ctx context.Context, msg []byte) error { - return conn.ws.Write(ctx, websocket.MessageText, msg) +func (conn *Connection) HasWebsocketConnection() bool { + return conn.ws != nil } -func (conn *Connection) Read(ctx context.Context) ([]byte, error) { +func (conn *Connection) SetWebsocketConnection(ws *websocket.Conn) { + if ws == nil { + return + } + + conn.ws = ws + + select { + case conn.wsConnectionEstablished <- true: + default: + } + + go func() { + for { + _, msg, _ := conn.ws.Read(conn.ctx) + conn.buffer.Insert(string(msg)) + } + }() +} + +func (conn *Connection) Write(msg []byte) error { + if conn.ws == nil { //if ws is not yet set, we wait for it + <-conn.wsConnectionEstablished + } + + log.Printf("Writing message: %s", string(msg)) + return conn.ws.Write(conn.ctx, websocket.MessageText, msg) +} + +func (conn *Connection) Read() ([]byte, error) { msg, err := conn.buffer.Get() if err != nil { + conn.ws = nil return nil, err // Tell game-handler that connection was lost } @@ -60,4 +84,5 @@ func (conn *Connection) Read(ctx context.Context) ([]byte, error) { func (conn *Connection) Close(msg string) { conn.ws.Close(websocket.StatusCode(400), msg) + conn.ws = nil } diff --git a/main.go b/main.go index 9ed2096..fbc24f0 100644 --- a/main.go +++ b/main.go @@ -137,7 +137,7 @@ func waitForAndHandlePlayerID(ctx context.Context, conn *websocket.Conn) { return } - log.Println("read from websocket: ", msgType, string(msg), err) + log.Println("read from websocket endpoint: ", msgType, string(msg), err) var info api.PlayerInfo err = json.Unmarshal(msg, &info) @@ -149,14 +149,18 @@ func waitForAndHandlePlayerID(ctx context.Context, conn *websocket.Conn) { } lobby := lobbies.GetLobbyRegistry().GetLobbyByUUID(*info.LobbyID) + if lobby == nil { + conn.Close(websocket.StatusCode(400), "lobby not found") + } + player, found := lobby.GetPlayerByUUID(*info.PlayerID) if !found { conn.Close(websocket.StatusCode(400), "player not found") return } - if player.Conn != nil { + if player.Conn.HasWebsocketConnection() { player.Conn.Close("closing existing connection") } - player.SetConnection(ctx, conn) + player.SetWebsocketConnection(ctx, conn) log.Println("player after setting connection: ", player) }