Compare commits

...

2 Commits

Author SHA1 Message Date
45fac78e06 Introduce method to send status of board and player 2023-12-09 14:45:57 +01:00
cce0aa8162 Handle reconnection
reconnection works now if the rejoining player enters the passphrase
again.

Some bugs are still happening:
1. The rejoining client is not told the state of the board
2. Invalid moves are not handled by the client (not sure why though)
3. The still-connected client should be told, that the opponent
   disconnected. Then the client should show the passphrase again
2023-11-27 00:17:07 +01:00
6 changed files with 117 additions and 19 deletions

View File

@ -6,16 +6,18 @@ import (
)
type WebsocketMessage struct {
Type MessageType `json:"messageType"`
Move *types.Move `json:"move,omitempty"`
Color *types.ChessColor `json:"color,omitempty"`
Reason *string `json:"reason,omitempty"`
Position *string `json:"position,omitempty"`
Type MessageType `json:"messageType"`
Move *types.Move `json:"move,omitempty"`
TurnColor *types.ChessColor `json:"turnColor,omitempty"`
PlayerColor *types.ChessColor `json:"playerColor,omitempty"`
Reason *string `json:"reason,omitempty"`
Position *string `json:"position,omitempty"`
}
type MessageType string
const (
PositionMessage MessageType = "position"
MoveMessage MessageType = "move"
InvalidMoveMessage MessageType = "invalidMove"
ColorDetermined MessageType = "colorDetermined"
@ -36,7 +38,7 @@ func (m WebsocketMessage) IsValidMoveMessage() bool {
}
func GetColorDeterminedMessage(color types.ChessColor) ([]byte, error) {
return json.Marshal(WebsocketMessage{Type: ColorDetermined, Color: &color})
return json.Marshal(WebsocketMessage{Type: ColorDetermined, TurnColor: &color})
}
func GetInvalidMoveMessage(move types.Move, reason string) ([]byte, error) {

View File

@ -1,12 +1,15 @@
package chess
import (
"context"
"log"
"math/rand"
"mchess_server/api"
"mchess_server/types"
"github.com/google/uuid"
"github.com/samber/lo"
"nhooyr.io/websocket"
)
type Game struct {
@ -15,6 +18,7 @@ type Game struct {
players []*Player
currentTurnPlayer *Player
gameState int
isBeingHandled bool
}
const (
@ -57,16 +61,28 @@ func (game *Game) prepare() {
game.players[0].color = types.White
game.players[1].color = types.Black
game.players[0].SetDisconnectCallback(game.playerDisconnected)
game.players[1].SetDisconnectCallback(game.playerDisconnected)
err := game.notifyPlayersAboutGameStart()
if err != nil {
return
}
}
func (game *Game) StartHandling() {
if game.isBeingHandled {
return
}
game.isBeingHandled = true
go game.Handle()
}
func (game *Game) Handle() {
defer game.killGame()
game.prepare()
game.prepare()
var receivedMove types.Move
var err error
@ -161,3 +177,15 @@ func (game Game) broadcastMove(move types.Move) error {
}
return nil
}
func (game *Game) playerDisconnected(p *Player) {
log.Println(string(p.color), " disconnected")
playerStillInGame := lo.Filter(game.players, func(player *Player, _ int) bool {
return player.color != p.color
})
game.players = playerStillInGame
}
func (game *Game) SetWebsocketConnectionFor(ctx context.Context, p *Player, ws *websocket.Conn) {
p.SetWebsocketConnectionAndSendBoardState(ctx, ws, game.board.PGN(), game.board.colorToMove)
}

View File

@ -6,7 +6,7 @@ import (
"errors"
"log"
"mchess_server/api"
conn "mchess_server/connection"
"mchess_server/connection"
"mchess_server/types"
"github.com/google/uuid"
@ -14,18 +14,20 @@ import (
)
type Player struct {
Uuid uuid.UUID
Conn *conn.Connection
InGame bool
color types.ChessColor
Uuid uuid.UUID
Conn *connection.Connection
color types.ChessColor
disconnectCallback func(p *Player)
}
func NewPlayer(uuid uuid.UUID) *Player {
return &Player{
Uuid: uuid,
Conn: conn.NewConnection(conn.WithContext(context.Background())),
InGame: false,
player := &Player{
Uuid: uuid,
Conn: connection.NewConnection(
connection.WithContext(context.Background())),
}
return player
}
func (p Player) HasWebsocketConnection() bool {
@ -36,6 +38,51 @@ func (p *Player) SetWebsocketConnection(ctx context.Context, ws *websocket.Conn)
p.Conn.SetWebsocketConnection(ws)
}
func (p *Player) SetWebsocketConnectionAndSendBoardState(
ctx context.Context,
ws *websocket.Conn,
boardPosition string,
turnColor types.ChessColor,
) {
p.SetWebsocketConnection(ctx, ws)
p.SendPosition(boardPosition, turnColor)
}
func (p *Player) SetDisconnectCallback(cb func(*Player)) {
// Todo: Fucking complicated
p.Conn.SetDisconnectCallback(p.PlayerDisconnectedCallback)
p.disconnectCallback = cb
}
func (p *Player) PlayerDisconnectedCallback() {
p.disconnectCallback(p)
}
func (p *Player) SendPosition(boardPosition string, turnColor types.ChessColor) error {
var pColor = p.color
if p.color == "" { // we default to white if we do not know the color yet
pColor = types.White
}
messageToSend, err := json.Marshal(api.WebsocketMessage{
Type: api.PositionMessage,
TurnColor: &turnColor,
PlayerColor: &pColor,
Position: &boardPosition,
})
if err != nil {
log.Println("Error while marshalling: ", err)
return err
}
err = p.writeMessage(messageToSend)
if err != nil {
log.Println("Error during message writing:", err)
return err
}
return nil
}
func (p *Player) SendMoveAndPosition(move types.Move, boardPosition string) error {
messageToSend, err := json.Marshal(api.WebsocketMessage{
Type: api.MoveMessage,

View File

@ -12,6 +12,7 @@ type Connection struct {
wsConnectionEstablished chan bool
ctx context.Context
buffer MessageBuffer
disconnectCallback func()
}
func NewConnection(options ...func(*Connection)) *Connection {
@ -39,6 +40,18 @@ func WithContext(ctx context.Context) func(*Connection) {
}
}
func WithDisconnectCallback(cb func()) func(*Connection) {
return func(c *Connection) {
if cb != nil {
c.disconnectCallback = cb
}
}
}
func (conn *Connection) SetDisconnectCallback(cb func()) {
conn.disconnectCallback = cb
}
func (conn *Connection) HasWebsocketConnection() bool {
return conn.ws != nil
}
@ -57,7 +70,14 @@ func (conn *Connection) SetWebsocketConnection(ws *websocket.Conn) {
go func() {
for {
_, msg, _ := conn.ws.Read(conn.ctx)
_, msg, err := conn.ws.Read(conn.ctx)
if err != nil {
log.Println("while reading from websocket: %w", err)
if conn.disconnectCallback != nil {
conn.disconnectCallback()
}
return
}
conn.buffer.Insert(string(msg))
}
}()

View File

@ -32,7 +32,7 @@ func newEmptyLobbyWithPassphrase() *Lobby {
func (l *Lobby) AddPlayerAndStartGameIfFull(player *chess.Player) {
l.Game.AddPlayersToGame(player)
if l.IsFull() {
go l.Game.Handle()
l.Game.StartHandling()
}
}

View File

@ -151,6 +151,7 @@ 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")
return
}
player, found := lobby.GetPlayerByUUID(*info.PlayerID)
@ -161,6 +162,6 @@ func waitForAndHandlePlayerID(ctx context.Context, conn *websocket.Conn) {
if player.Conn.HasWebsocketConnection() {
player.Conn.Close("closing existing connection")
}
player.SetWebsocketConnection(ctx, conn)
lobby.Game.SetWebsocketConnectionFor(ctx, player, conn)
log.Println("player after setting connection: ", player)
}