maybe joining handler exists now
This commit is contained in:
parent
f2ba9849f1
commit
ffc39c9f8a
2
game.go
2
game.go
@ -21,7 +21,7 @@ type action struct {
|
|||||||
|
|
||||||
// gameActor is an actor that updates a game's state and relays changes to all
|
// gameActor is an actor that updates a game's state and relays changes to all
|
||||||
// observers.
|
// observers.
|
||||||
func gameActor(ctx context.Context, g *game.Game, dealer, chall person, join chan person) {
|
func gameActor(ctx context.Context, g *game.Game, dealer, chall person, join <-chan person) {
|
||||||
// Games should generally be on the order of minutes. A four hour game is
|
// Games should generally be on the order of minutes. A four hour game is
|
||||||
// definitely expired.
|
// definitely expired.
|
||||||
ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errGameExpired)
|
ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errGameExpired)
|
||||||
|
79
server.go
79
server.go
@ -8,24 +8,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
|
"nhooyr.io/websocket/wsjson"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/studio/shotgun/game"
|
||||||
"git.sunturtle.xyz/studio/shotgun/lobby"
|
"git.sunturtle.xyz/studio/shotgun/lobby"
|
||||||
"git.sunturtle.xyz/studio/shotgun/player"
|
"git.sunturtle.xyz/studio/shotgun/player"
|
||||||
"git.sunturtle.xyz/studio/shotgun/serve"
|
"git.sunturtle.xyz/studio/shotgun/serve"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
l *lobby.Lobby
|
l *lobby.Lobby
|
||||||
mu sync.Mutex
|
|
||||||
rooms map[lobby.GameID]*room
|
|
||||||
}
|
|
||||||
|
|
||||||
type room struct {
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pp []person
|
pp map[player.ID]*websocket.Conn
|
||||||
// NOTE(zeph): since only the players can ever see revealed shells, we
|
|
||||||
// could factor out the players into separate fields to save work on
|
|
||||||
// generating dtos for observers. hold that idea until it's needed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type person struct {
|
type person struct {
|
||||||
@ -33,27 +28,35 @@ type person struct {
|
|||||||
id player.ID
|
id player.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) room(id serve.GameID) *room {
|
func (s *Server) person(p player.ID) person {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
return s.rooms[id]
|
return person{conn: s.pp[p], id: p}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) join(id serve.GameID, p person) {
|
func (s *Server) accept(w http.ResponseWriter, r *http.Request, p player.ID) (person, error) {
|
||||||
s.mu.Lock()
|
conn, err := websocket.Accept(w, r, nil)
|
||||||
defer s.mu.Unlock()
|
if err != nil {
|
||||||
r := s.rooms[id]
|
return person{}, err
|
||||||
if r == nil {
|
|
||||||
r = &room{}
|
|
||||||
s.rooms[id] = r
|
|
||||||
}
|
}
|
||||||
r.pp = append(r.pp, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) close(id serve.GameID) {
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
delete(s.rooms, id)
|
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.
|
||||||
|
c.Close(websocket.StatusNormalClosure, "bye")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue connects players to games. The connection immediately upgrades.
|
// Queue connects players to games. The connection immediately upgrades.
|
||||||
@ -63,26 +66,40 @@ func (s *Server) Queue(w http.ResponseWriter, r *http.Request) {
|
|||||||
if p == (player.ID{}) {
|
if p == (player.ID{}) {
|
||||||
panic("missing player ID")
|
panic("missing player ID")
|
||||||
}
|
}
|
||||||
conn, err := websocket.Accept(w, r, nil)
|
person, err := s.accept(w, r, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
person := person{conn: conn, id: p}
|
s.joinAndServe(person)
|
||||||
go s.joinAndServe(person)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) joinAndServe(p person) {
|
func (s *Server) joinAndServe(p person) {
|
||||||
ctx, stop := context.WithTimeoutCause(context.Background(), 10*time.Minute, errQueueEmpty)
|
ctx, stop := context.WithTimeoutCause(context.Background(), 10*time.Minute, errQueueEmpty)
|
||||||
game := s.l.Queue(ctx, p.id)
|
id, chall, deal := s.l.Queue(ctx, p.id)
|
||||||
stop()
|
stop()
|
||||||
if game == (lobby.GameID{}) {
|
if id == (lobby.GameID{}) {
|
||||||
// Context canceled.
|
// Context canceled.
|
||||||
p.conn.Close(websocket.StatusNormalClosure, "sorry, queue is empty...")
|
p.conn.Close(websocket.StatusTryAgainLater, "sorry, queue is empty...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if deal {
|
||||||
|
g := game.New(p.id, chall)
|
||||||
|
other := s.person(chall)
|
||||||
|
// TODO(zeph): save the game state s.t. we can provide a join channel
|
||||||
|
go gameActor(ctx, g, p, other, nil)
|
||||||
|
}
|
||||||
|
// Reply with the game ID so they can share.
|
||||||
|
r := struct {
|
||||||
|
Game lobby.GameID `json:"game"`
|
||||||
|
}{
|
||||||
|
Game: id,
|
||||||
|
}
|
||||||
|
if err := wsjson.Write(context.TODO(), p.conn, r); err != nil {
|
||||||
|
// TODO(zeph): log
|
||||||
|
s.left(p.id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.join(game, p)
|
|
||||||
// TODO(zeph): need to broadcast to observers, need to listen for updates in a single goroutine, ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errQueueEmpty = errors.New("sorry, queue is empty")
|
var errQueueEmpty = errors.New("sorry, queue is empty")
|
||||||
|
Loading…
Reference in New Issue
Block a user