2024-01-21 05:35:21 -05:00
|
|
|
package lobby
|
2024-01-21 02:33:44 -05:00
|
|
|
|
|
|
|
import (
|
2024-01-27 10:43:55 -05:00
|
|
|
"context"
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2024-01-21 02:33:44 -05:00
|
|
|
|
2024-01-21 13:48:29 -05:00
|
|
|
"git.sunturtle.xyz/studio/shotgun/player"
|
2024-01-21 13:28:46 -05:00
|
|
|
"git.sunturtle.xyz/studio/shotgun/serve"
|
2024-01-21 02:33:44 -05:00
|
|
|
)
|
|
|
|
|
2024-01-21 13:28:46 -05:00
|
|
|
type GameID = serve.GameID
|
2024-01-21 02:33:44 -05:00
|
|
|
|
2024-01-27 10:43:55 -05:00
|
|
|
type matchMade struct {
|
|
|
|
match chan GameID
|
|
|
|
chall player.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
type match chan matchMade
|
|
|
|
|
|
|
|
// Lobby is a matchmaking service.
|
2024-01-21 02:33:44 -05:00
|
|
|
type Lobby struct {
|
2024-01-21 20:53:08 -05:00
|
|
|
// matches is dealers waiting for a match. It MUST be unbuffered.
|
|
|
|
matches chan match
|
2024-01-21 02:33:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func New() *Lobby {
|
|
|
|
return &Lobby{
|
2024-01-21 20:53:08 -05:00
|
|
|
matches: make(chan match),
|
2024-01-21 02:33:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-27 10:43:55 -05: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 02:33:44 -05:00
|
|
|
}
|