Add match listing API

This commit is contained in:
Trevor Slocum 2023-11-16 09:25:26 -08:00
parent 2493c76bdf
commit d0f8b45b0f
4 changed files with 78 additions and 16 deletions

View file

@ -288,6 +288,26 @@ func (g *serverGame) opponent(client *serverClient) *serverClient {
return nil
}
func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
if g.terminated() {
return nil
}
var playerCount int
if len(g.allowed1) != 0 && (len(playerName) == 0 || (!bytes.Equal(g.allowed1, playerName) && !bytes.Equal(g.allowed2, playerName))) {
playerCount = 2
} else {
playerCount = g.playerCount()
}
return &bgammon.GameListing{
ID: g.id,
Points: g.Points,
Password: len(g.password) != 0,
Players: playerCount,
Name: string(g.name),
}
}
func (g *serverGame) terminated() bool {
return g.client1 == nil && g.client2 == nil
}

View file

@ -13,11 +13,13 @@ func main() {
tcpAddress string
wsAddress string
debug int
debugCommands bool
rollStatistics bool
)
flag.StringVar(&tcpAddress, "tcp", "localhost:1337", "TCP listen address")
flag.StringVar(&wsAddress, "ws", "localhost:1338", "WebSocket listen address")
flag.IntVar(&debug, "debug", 0, "print debug information and serve pprof on specified port")
flag.BoolVar(&debugCommands, "debug-commands", false, "allow players to use restricted commands")
flag.BoolVar(&rollStatistics, "statistics", false, "print dice roll statistics and exit")
flag.Parse()
@ -36,6 +38,10 @@ func main() {
}()
}
if debugCommands {
allowDebugCommands = debugCommands
}
s := newServer()
if tcpAddress != "" {
s.listen("tcp", tcpAddress)

View file

@ -3,6 +3,7 @@ package main
import (
"bytes"
"crypto/rand"
"encoding/json"
"fmt"
"log"
"math/big"
@ -42,6 +43,10 @@ type server struct {
gamesLock sync.RWMutex
clientsLock sync.Mutex
gamesCache []byte
gamesCacheTime time.Time
gamesCacheLock sync.Mutex
}
func newServer() *server {
@ -59,6 +64,44 @@ func newServer() *server {
return s
}
func (s *server) cachedMatches() []byte {
s.gamesCacheLock.Lock()
defer s.gamesCacheLock.Unlock()
if time.Since(s.gamesCacheTime) < 5*time.Second {
return s.gamesCache
}
s.gamesLock.Lock()
defer s.gamesLock.Unlock()
var games []*bgammon.GameListing
for _, g := range s.games {
listing := g.listing(nil)
if listing == nil || listing.Password || listing.Players == 2 {
continue
}
games = append(games, listing)
}
s.gamesCacheTime = time.Now()
if len(games) == 0 {
s.gamesCache = []byte("[]")
return s.gamesCache
}
var err error
s.gamesCache, err = json.Marshal(games)
if err != nil {
log.Fatalf("failed to marshal %+v: %s", games, err)
}
return s.gamesCache
}
func (s *server) handleListMatches(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(s.cachedMatches())
}
func (s *server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
const bufferSize = 8
commands := make(chan []byte, bufferSize)
@ -84,7 +127,12 @@ func (s *server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
func (s *server) listenWebSocket(address string) {
log.Printf("Listening for WebSocket connections on %s...", address)
err := http.ListenAndServe(address, http.HandlerFunc(s.handleWebSocket))
mux := http.NewServeMux()
mux.HandleFunc("/matches", s.handleListMatches)
mux.HandleFunc("/", s.handleWebSocket)
err := http.ListenAndServe(address, mux)
log.Fatalf("failed to listen on %s: %s", address, err)
}
@ -456,23 +504,12 @@ COMMANDS:
ev := &bgammon.EventList{}
s.gamesLock.RLock()
var playerCount int
for _, g := range s.games {
if g.terminated() {
listing := g.listing(cmd.client.name)
if listing == nil {
continue
}
if len(g.allowed1) != 0 && !bytes.Equal(g.allowed1, cmd.client.name) && !bytes.Equal(g.allowed2, cmd.client.name) {
playerCount = 2
} else {
playerCount = g.playerCount()
}
ev.Games = append(ev.Games, bgammon.GameListing{
ID: g.id,
Points: g.Points,
Password: len(g.password) != 0,
Players: playerCount,
Name: string(g.name),
})
ev.Games = append(ev.Games, *listing)
}
s.gamesLock.RUnlock()

View file

@ -41,7 +41,6 @@ type EventSay struct {
}
type GameListing struct {
Event
ID int
Password bool
Points int