shotgun/lobby/lobby.go

88 lines
2.0 KiB
Go
Raw Normal View History

2024-01-21 04:35:21 -06:00
package lobby
2024-01-21 01:33:44 -06:00
import (
2024-01-27 09:43:55 -06:00
"context"
"github.com/google/uuid"
2024-01-21 01:33:44 -06:00
2024-01-21 12:48:29 -06:00
"git.sunturtle.xyz/studio/shotgun/player"
2024-01-21 12:28:46 -06:00
"git.sunturtle.xyz/studio/shotgun/serve"
2024-01-21 01:33:44 -06:00
)
2024-01-21 12:28:46 -06:00
type GameID = serve.GameID
2024-01-21 01:33:44 -06:00
2024-01-27 09:43:55 -06:00
type matchMade struct {
match chan GameID
chall player.ID
}
type match chan matchMade
// Lobby is a matchmaking service.
2024-01-21 01:33:44 -06:00
type Lobby struct {
2024-01-21 19:53:08 -06:00
// matches is dealers waiting for a match. It MUST be unbuffered.
matches chan match
2024-01-21 01:33:44 -06:00
}
func New() *Lobby {
return &Lobby{
2024-01-21 19:53:08 -06:00
matches: make(chan match),
2024-01-21 01:33:44 -06:00
}
}
2024-01-27 09:43:55 -06:00
// Queue waits for a match and returns a unique ID for it.
func (l *Lobby) Queue(ctx context.Context, p player.ID) (id GameID, chall player.ID, deal bool) {
select {
case m := <-l.matches:
// We found a dealer waiting for a match.
r := matchMade{
match: make(chan GameID, 1),
chall: p,
}
// We don't need to check the context here because the challenger
// channel is buffered and we have exclusive send access on it.
m <- r
// We do need to check the context here in case they disappeared.
select {
case <-ctx.Done():
return GameID{}, player.ID{}, false
case id := <-r.match:
return id, p, false
}
default: // do nothing
}
// We're a new dealer.
m := make(match, 1)
select {
case <-ctx.Done():
return GameID{}, player.ID{}, false
case l.matches <- m:
// Our match is submitted. Move on.
case m := <-l.matches:
// We might have become a dealer at the same time someone else did.
// We created our match, but we can try to get theirs as well and
// never send ours, since l.matches is unbuffered.
r := matchMade{
match: make(chan GameID, 1),
chall: p,
}
m <- r
select {
case <-ctx.Done():
return GameID{}, player.ID{}, false
case id := <-r.match:
return id, p, false
}
}
select {
case <-ctx.Done():
return GameID{}, player.ID{}, false
case r := <-m:
// Got our challenger. Create the game and send the ID back.
id := GameID(uuid.New())
// Don't need to check context because the match channel is buffered.
r.match <- id
return id, r.chall, true
}
2024-01-21 01:33:44 -06:00
}