diff --git a/cmd/bgammon-server/game.go b/cmd/bgammon-server/game.go index e3691e1..79bfbbe 100644 --- a/cmd/bgammon-server/game.go +++ b/cmd/bgammon-server/game.go @@ -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 } diff --git a/cmd/bgammon-server/main.go b/cmd/bgammon-server/main.go index 9eeb91e..1e53a57 100644 --- a/cmd/bgammon-server/main.go +++ b/cmd/bgammon-server/main.go @@ -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) diff --git a/cmd/bgammon-server/server.go b/cmd/bgammon-server/server.go index 1e112f1..f110891 100644 --- a/cmd/bgammon-server/server.go +++ b/cmd/bgammon-server/server.go @@ -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() diff --git a/event.go b/event.go index 76d0f68..121d4df 100644 --- a/event.go +++ b/event.go @@ -41,7 +41,6 @@ type EventSay struct { } type GameListing struct { - Event ID int Password bool Points int