2022-02-12 23:35:30 +02:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"github.com/gliderlabs/ssh"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PlayerType is the type of a player in a chess game.
|
|
|
|
type PlayerType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
whitePlayer PlayerType = iota
|
|
|
|
blackPlayer
|
|
|
|
observerPlayer
|
|
|
|
)
|
|
|
|
|
|
|
|
// String implements the Stringer interface.
|
|
|
|
func (pt PlayerType) String() string {
|
|
|
|
switch pt {
|
|
|
|
case whitePlayer:
|
|
|
|
return "White"
|
|
|
|
case blackPlayer:
|
|
|
|
return "Black"
|
|
|
|
case observerPlayer:
|
|
|
|
return "Observer"
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Player is a player in a chess game who belongs to a room, has a ssh session
|
|
|
|
// and a bubble tea program.
|
|
|
|
type Player struct {
|
2022-02-15 03:21:12 +02:00
|
|
|
room *Room
|
|
|
|
session ssh.Session
|
|
|
|
program *tea.Program
|
|
|
|
game *SharedGame
|
|
|
|
ptype PlayerType
|
|
|
|
key PublicKey
|
|
|
|
once sync.Once
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// String implements the Stringer interface.
|
|
|
|
func (p *Player) String() string {
|
2022-02-15 03:21:12 +02:00
|
|
|
u := p.session.User()
|
|
|
|
return fmt.Sprintf("%s (%s)", u, p.ptype)
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Position returns the player's board FEN position.
|
|
|
|
func (p *Player) Position() string {
|
2022-02-15 03:21:12 +02:00
|
|
|
if p.game != nil && p.game.game != nil {
|
|
|
|
return p.game.game.Position()
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send sends a message to the bubble tea program.
|
|
|
|
func (p *Player) Send(m tea.Msg) {
|
2022-02-15 03:21:12 +02:00
|
|
|
if p.program != nil {
|
|
|
|
p.program.Send(m)
|
2022-02-12 23:35:30 +02:00
|
|
|
} else {
|
|
|
|
log.Printf("error sending message to player, program is nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes data to the ssh session.
|
|
|
|
func (p *Player) Write(b []byte) (int, error) {
|
2022-02-15 03:21:12 +02:00
|
|
|
return p.session.Write(b)
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// WriteString writes a string to the ssh session.
|
|
|
|
func (p *Player) WriteString(s string) (int, error) {
|
2022-02-15 03:21:12 +02:00
|
|
|
return p.session.Write([]byte(s))
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the the bubble tea program and deletes the player from the room.
|
|
|
|
func (p *Player) Close() error {
|
|
|
|
p.once.Do(func() {
|
2022-02-15 03:21:12 +02:00
|
|
|
defer delete(p.room.players, p.key.String())
|
|
|
|
if p.program != nil {
|
|
|
|
p.program.Kill()
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
2022-02-15 03:21:12 +02:00
|
|
|
p.session.Close()
|
2022-02-12 23:35:30 +02:00
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartGame starts the bubble tea program.
|
|
|
|
func (p *Player) StartGame() {
|
2022-02-15 03:21:12 +02:00
|
|
|
_, winch, _ := p.session.Pty()
|
2022-02-12 23:35:30 +02:00
|
|
|
errc := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
select {
|
|
|
|
case err := <-errc:
|
|
|
|
log.Printf("error starting program %s", err)
|
|
|
|
case w := <-winch:
|
2022-02-15 03:21:12 +02:00
|
|
|
if p.program != nil {
|
|
|
|
p.program.Send(tea.WindowSizeMsg{Width: w.Width, Height: w.Height})
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
2022-02-15 03:21:12 +02:00
|
|
|
case <-p.session.Context().Done():
|
2022-02-12 23:35:30 +02:00
|
|
|
p.Close()
|
|
|
|
}
|
|
|
|
}()
|
2022-02-15 03:21:12 +02:00
|
|
|
defer p.room.SendMsg(NoteMsg(fmt.Sprintf("%s left the room", p)))
|
|
|
|
m, err := p.program.StartReturningModel()
|
2022-02-12 23:35:30 +02:00
|
|
|
if m != nil {
|
2022-02-15 03:21:12 +02:00
|
|
|
p.game = m.(*SharedGame)
|
2022-02-12 23:35:30 +02:00
|
|
|
}
|
|
|
|
errc <- err
|
|
|
|
p.Close()
|
|
|
|
}
|