Skip to content

Commit

Permalink
Merge pull request #145 from joshzcold/fix/room-timeout
Browse files Browse the repository at this point in the history
fix(goroutine): add missing loop into gameTimeout
  • Loading branch information
joshzcold authored Jan 11, 2025
2 parents e0aa57b + 6c9c61b commit c972619
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 27 deletions.
1 change: 1 addition & 0 deletions backend/api/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,6 @@ func NewGame(roomCode string) room {
Tick: time.Now().UTC().UnixMilli(),
RoundStartTime: time.Now().UTC().UnixMilli(),
},
cleanup: make(chan struct{}),
}
}
54 changes: 33 additions & 21 deletions backend/api/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const ()
var roomLetterLength = 4
var roomLetters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

var cfg struct {
roomTimeoutSeconds int64
}

func SetConfig(timeoutSeconds int64) {
cfg.roomTimeoutSeconds = timeoutSeconds
}

// MakeRoom return a room code from random pick of characters of room code length
func roomCode() string {
b := make([]rune, roomLetterLength)
Expand Down Expand Up @@ -60,31 +68,34 @@ func (p *RegisteredClient) pingInterval() error {

func (r *room) gameTimeout() error {
ticker := time.NewTicker(60 * time.Second)
defer func() {
ticker.Stop()
}()
select {
case <-ticker.C:
oneHour := 1 * time.Hour
oneHourAgo := time.Now().UTC().Add(-oneHour).UnixMilli()
if r.Game.Tick > 0 && r.Game.Tick < oneHourAgo {
log.Println("clearing room, no activity for 1 hour: ", r.Game.Room)
message, err := NewSendQuit()
if err != nil {
return fmt.Errorf(" %w", err)
}
r.Hub.broadcast <- message
message, err = NewSendError(GameError{code: GAME_CLOSED})
if err != nil {
return fmt.Errorf(" %w", err)
defer ticker.Stop()

for {
select {
case <-ticker.C:
timeout := time.Duration(cfg.roomTimeoutSeconds) * time.Second
timeoutAgo := time.Now().UTC().Add(-timeout).UnixMilli()
if r.Game.Tick > 0 && r.Game.Tick < timeoutAgo {
log.Println("clearing room, no activity for", cfg.roomTimeoutSeconds, "seconds:", r.Game.Room)
message, err := NewSendQuit()
if err != nil {
return fmt.Errorf(" %w", err)
}
r.Hub.broadcast <- message
message, err = NewSendError(GameError{code: GAME_CLOSED})
if err != nil {
return fmt.Errorf(" %w", err)
}
r.Hub.broadcast <- message
store.deleteLogo(r.Game.Room)
store.deleteRoom(r.Game.Room)
return nil
}
r.Hub.broadcast <- message
store.deleteLogo(r.Game.Room)
store.deleteRoom(r.Game.Room)
// If host quits, remove loop
case <-r.cleanup:
return nil
}
}
return nil
}

type RegisteredClient struct {
Expand All @@ -104,4 +115,5 @@ type room struct {
Game *game `json:"game"`
// Assign to ws Hub when hosting room
roomConnections
cleanup chan struct{}
}
2 changes: 2 additions & 0 deletions backend/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func quitHost(room *room, event *Event) GameError {
if room.Hub.stop != nil {
room.Hub.stop <- true
}
// Signal cleanup channel to stop the room timeout goroutine
close(room.cleanup)
// Remove room
s.deleteRoom(event.Room)
s.deleteLogo(event.Room)
Expand Down
26 changes: 20 additions & 6 deletions backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,25 @@ import (
"log"
"net/http"
"os"
"strconv"

"github.com/joshzcold/Cold-Friendly-Feud/api"
)

var cfg = struct {
addr string
store string
}{}
addr string
store string
roomTimeoutSeconds int64
}{
addr: ":8080",
store: "memory",
roomTimeoutSeconds: 86400,
}

func flags() {
flag.StringVar(&cfg.addr, "listen_address", ":8080", "Address for server to bind to.")
flag.StringVar(&cfg.store, "game_store", "memory", "Choice of storage medium of the game")
flag.StringVar(&cfg.addr, "listen_address", cfg.addr, "Address for server to bind to.")
flag.StringVar(&cfg.store, "game_store", cfg.store, "Choice of storage medium of the game")
flag.Int64Var(&cfg.roomTimeoutSeconds, "room_timeout_seconds", cfg.roomTimeoutSeconds, "Seconds before inactive rooms are cleaned up")
flag.Parse()

if envAddr := os.Getenv("LISTEN_ADDRESS"); envAddr != "" {
Expand All @@ -27,10 +34,17 @@ func flags() {
if envGameStore := os.Getenv("GAME_STORE"); envGameStore != "" {
cfg.store = envGameStore
}

if envTimeout := os.Getenv("ROOM_TIMEOUT_SECONDS"); envTimeout != "" {
if timeout, err := strconv.ParseInt(envTimeout, 10, 64); err == nil {
cfg.roomTimeoutSeconds = timeout
}
}
}

func main() {
flags()
api.SetConfig(cfg.roomTimeoutSeconds)
err := api.NewGameStore(cfg.store)
if err != nil {
log.Panicf("Error: unable initalize store: %s", err)
Expand Down Expand Up @@ -59,7 +73,7 @@ func main() {
api.FetchLogo(httpWriter, roomCode)
})
log.Printf("Server listening on %s", cfg.addr)
err = http.ListenAndServe(*&cfg.addr, nil)
err = http.ListenAndServe(cfg.addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
Expand Down

0 comments on commit c972619

Please sign in to comment.