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
This commit is contained in:
Marco 2023-11-27 00:12:42 +01:00
parent efefa4ced5
commit cce0aa8162
4 changed files with 67 additions and 11 deletions

View File

@ -7,6 +7,7 @@ import (
"mchess_server/types"
"github.com/google/uuid"
"github.com/samber/lo"
)
type Game struct {
@ -15,6 +16,7 @@ type Game struct {
players []*Player
currentTurnPlayer *Player
gameState int
isBeingHandled bool
}
const (
@ -57,16 +59,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 +175,11 @@ func (game Game) broadcastMove(move types.Move) error {
}
return nil
}
func (game *Game) playerDisconnected(p *Player) {
log.Println(string(p.color), " disconnected")
playerLeft := lo.Filter(game.players, func(player *Player, _ int) bool {
return player.color != p.color
})
game.players = playerLeft
}

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,22 @@ import (
)
type Player struct {
Uuid uuid.UUID
Conn *conn.Connection
InGame bool
color types.ChessColor
Uuid uuid.UUID
Conn *connection.Connection
InGame bool
color types.ChessColor
disconnectCallback func(p *Player)
}
func NewPlayer(uuid uuid.UUID) *Player {
return &Player{
Uuid: uuid,
Conn: conn.NewConnection(conn.WithContext(context.Background())),
player := &Player{
Uuid: uuid,
Conn: connection.NewConnection(
connection.WithContext(context.Background())),
InGame: false,
}
return player
}
func (p Player) HasWebsocketConnection() bool {
@ -36,6 +40,16 @@ func (p *Player) SetWebsocketConnection(ctx context.Context, ws *websocket.Conn)
p.Conn.SetWebsocketConnection(ws)
}
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) 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()
}
}