exchange syncing persons in matchmaking

This commit is contained in:
Branden J Brown 2024-02-04 09:42:42 -06:00
parent 82aba814e6
commit 658634c0db
3 changed files with 14 additions and 57 deletions

View File

@ -98,7 +98,6 @@ func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <-
broadcast(ctx, g, dealer, chall, obs, dl)
if g.MatchWinner() != nil {
gameOver(ctx, dealer, chall, obs)
// TODO(zeph): server needs to know the players are gone
return
}
g.NextRound()
@ -161,14 +160,12 @@ func playerActor(ctx context.Context, p person, actions chan<- action) {
}
func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []observer, deadline time.Time) {
// TODO(zeph): this probably should return an error or some other signal
// if a player drops so that the actor knows to quit
// NOTE(zeph): If our sends to players fail, then the socket will close,
// causing the readers to fail and send concede messages. No leak.
if err := wsjson.Write(ctx, dealer.conn, g.DTO(dealer.id, deadline)); err != nil {
// TODO(zeph): concede, but we need to be careful not to recurse
slog.WarnContext(ctx, "lost dealer", "player", dealer.id, "err", err.Error())
}
if err := wsjson.Write(ctx, chall.conn, g.DTO(chall.id, deadline)); err != nil {
// TODO(zeph): concede, but we need to be careful not to recurse
slog.WarnContext(ctx, "lost challenger", "player", chall.id, "err", err.Error())
}
if len(obs) == 0 {
@ -183,7 +180,6 @@ func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []o
}
func gameOver(ctx context.Context, dealer, chall person, obs []observer) {
// TODO(zeph): need to communicate to the server that these have gone away
go dealer.conn.Close(websocket.StatusNormalClosure, "match ended")
go chall.conn.Close(websocket.StatusNormalClosure, "match ended")
for _, p := range obs {

View File

@ -10,7 +10,6 @@ import (
"github.com/go-chi/chi/v5/middleware"
"github.com/google/uuid"
"gitlab.com/zephyrtronium/sq"
"nhooyr.io/websocket"
"git.sunturtle.xyz/studio/shotgun/lobby"
"git.sunturtle.xyz/studio/shotgun/player"
@ -43,11 +42,9 @@ func main() {
return
}
s := Server{
l: lobby.New[uuid.UUID, player.ID](),
l: lobby.New[uuid.UUID, matchingPerson](),
creds: db,
sessions: db,
pp: map[player.ID]*websocket.Conn{},
j: map[uuid.UUID]chan person{},
}
r := chi.NewRouter()

View File

@ -6,7 +6,6 @@ import (
"errors"
"log/slog"
"net/http"
"sync"
"time"
"github.com/google/uuid"
@ -20,14 +19,10 @@ import (
)
type Server struct {
l *lobby.Lobby[uuid.UUID, player.ID]
l *lobby.Lobby[uuid.UUID, matchingPerson]
creds db
sessions db
mu sync.Mutex
pp map[player.ID]*websocket.Conn
j map[uuid.UUID]chan person
}
type db interface {
@ -40,10 +35,9 @@ type person struct {
id player.ID
}
func (s *Server) person(p player.ID) person {
s.mu.Lock()
defer s.mu.Unlock()
return person{conn: s.pp[p], id: p}
type matchingPerson struct {
sync chan struct{}
person
}
func (s *Server) accept(w http.ResponseWriter, r *http.Request, p player.ID) (person, error) {
@ -52,27 +46,9 @@ func (s *Server) accept(w http.ResponseWriter, r *http.Request, p player.ID) (pe
return person{}, err
}
slog.Debug("upgraded", "player", p)
s.mu.Lock()
defer s.mu.Unlock()
s.pp[p] = conn
return person{conn: conn, id: p}, nil
}
func (s *Server) left(p player.ID) {
s.mu.Lock()
// NOTE(zeph): neither map index nor map delete can panic, so this critical
// section does not need a defer
c := s.pp[p]
delete(s.pp, p)
s.mu.Unlock()
if c != nil {
// We don't care about the error here since the connection is leaving.
// It's probably already closed anyway.
slog.Debug("leaving", "player", p)
c.Close(websocket.StatusNormalClosure, "bye")
}
}
func (s *Server) Register(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
slog := slog.With(
@ -208,8 +184,10 @@ func (s *Server) joinAndServe(p person) {
stop()
}
}()
id, chall, deal := s.l.Queue(ctx, uuid.New, p.id)
mp := matchingPerson{sync: make(chan struct{}), person: p}
id, chall, deal := s.l.Queue(ctx, uuid.New, mp)
<-ch
close(mp.sync)
stop()
if id == uuid.Nil {
// Context canceled.
@ -218,24 +196,10 @@ func (s *Server) joinAndServe(p person) {
}
if deal {
ch := make(chan person, 2)
s.mu.Lock()
s.j[id] = ch
s.mu.Unlock()
g := game.New(p.id, chall)
other := s.person(chall)
go gameActor(context.TODO(), g, p, other, ch)
g := game.New(p.id, chall.id)
<-chall.sync
go gameActor(context.TODO(), g, p, chall.person, ch)
ch <- p
} else {
// very gross, but i just want this to exist
for {
s.mu.Lock()
ch := s.j[id]
s.mu.Unlock()
if ch != nil {
ch <- p
break
}
}
}
// Reply with the game ID so they can share.
r := serve.GameStart{
@ -244,7 +208,7 @@ func (s *Server) joinAndServe(p person) {
}
if err := wsjson.Write(context.TODO(), p.conn, r); err != nil {
slog.WarnContext(ctx, "got a game but player dropped", "game", id, "player", p.id)
s.left(p.id)
p.conn.Close(websocket.StatusNormalClosure, "looks like you dropped")
return
}
}