From e6177df5d8d6889b66de2883b5678e6c69cc278e Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Tue, 1 Jul 2025 09:00:59 +0300 Subject: [PATCH] Feat: turn timer --- components/room.html | 1 + components/turntimer.html | 5 ++++ handlers/actions.go | 4 +-- handlers/elements.go | 2 +- handlers/game.go | 12 +++++--- handlers/timer.go | 60 +++++++++++++++++++++++++++++++++++++++ models/main.go | 8 +++--- 7 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 components/turntimer.html create mode 100644 handlers/timer.go diff --git a/components/room.html b/components/room.html index 9feab4f..207e242 100644 --- a/components/room.html +++ b/components/room.html @@ -25,6 +25,7 @@
{{if .Room.IsRunning}}

Turn of the {{.Room.TeamTurn}} team

+{{template "turntimer" .Room}} {{if .Room.MimeDone}}

Waiting for guessers

Given Clue: "{{.Room.FetchLastClueWord}}"

diff --git a/components/turntimer.html b/components/turntimer.html new file mode 100644 index 0000000..791571d --- /dev/null +++ b/components/turntimer.html @@ -0,0 +1,5 @@ +{{define "turntimer"}} +
+ Timer: {{.Settings.TurnSecondsLeft}} +
+{{end}} diff --git a/handlers/actions.go b/handlers/actions.go index 30e439f..7d56390 100644 --- a/handlers/actions.go +++ b/handlers/actions.go @@ -84,8 +84,8 @@ func saveFullInfo(fi *models.FullInfo) error { return nil } -func notifyBotIfNeeded(fi *models.FullInfo) { - if botName := fi.Room.WhichBotToMove(); botName != "" { +func notifyBotIfNeeded(room *models.Room) { + if botName := room.WhichBotToMove(); botName != "" { log.Debug("got botname", "name", botName, "channel_len", len(llmapi.SignalChanMap[botName])) llmapi.SignalChanMap[botName] <- true log.Debug("after sending signal", "name", botName) diff --git a/handlers/elements.go b/handlers/elements.go index 201a792..96821f4 100644 --- a/handlers/elements.go +++ b/handlers/elements.go @@ -159,7 +159,7 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { return } // get mime bot for opp team and notify it - notifyBotIfNeeded(fi) + notifyBotIfNeeded(fi.Room) notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil { log.Error("failed to execute cardword template", "error", err) diff --git a/handlers/game.go b/handlers/game.go index df76741..57648df 100644 --- a/handlers/game.go +++ b/handlers/game.go @@ -9,6 +9,7 @@ import ( "net/http" "strconv" "strings" + "time" ) func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { @@ -122,6 +123,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { } fi.Room.ChangeTurn() fi.Room.MimeDone = false + StopTurnTimer(fi.Room.ID) if err := saveFullInfo(fi); err != nil { abortWithError(w, err.Error()) return @@ -132,7 +134,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - notifyBotIfNeeded(fi) + notifyBotIfNeeded(fi.Room) notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { log.Error("failed to execute base template", "error", err) @@ -181,7 +183,7 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - notifyBotIfNeeded(fi) + notifyBotIfNeeded(fi.Room) // to update only the room that should be updated notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") // notify(models.NotifyBacklogPrefix+fi.Room.ID, "game started") @@ -283,9 +285,11 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) { fi.Room.ThisTurnLimit = 9 } fi.Room.OpenedThisTurn = 0 + fi.Room.Settings.TurnSecondsLeft = fi.Room.Settings.RoundTime + StartTurnTimer(fi.Room.ID, time.Duration(fi.Room.Settings.RoundTime)*time.Second) log.Debug("given clue", "clue", clue, "limit", fi.Room.ThisTurnLimit) notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num) - notifyBotIfNeeded(fi) + notifyBotIfNeeded(fi.Room) if err := saveFullInfo(fi); err != nil { abortWithError(w, err.Error()) return @@ -298,5 +302,5 @@ func HandleRenotifyBot(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - notifyBotIfNeeded(fi) + notifyBotIfNeeded(fi.Room) } diff --git a/handlers/timer.go b/handlers/timer.go new file mode 100644 index 0000000..f188616 --- /dev/null +++ b/handlers/timer.go @@ -0,0 +1,60 @@ +package handlers + +import ( + "gralias/models" + "sync" + "time" +) + +var ( + timers = make(map[string]*time.Ticker) + mu sync.Mutex +) + +func StartTurnTimer(roomID string, duration time.Duration) { + mu.Lock() + defer mu.Unlock() + + if _, exists := timers[roomID]; exists { + return // Timer already running + } + ticker := time.NewTicker(1 * time.Second) + timers[roomID] = ticker + go func() { + for range ticker.C { + room, err := getRoomByID(roomID) + if err != nil { + log.Error("failed to get room by id", "error", err) + StopTurnTimer(roomID) + return + } + if room.Settings.TurnSecondsLeft == 0 { + log.Info("turn time is over", "room_id", roomID) + room.ChangeTurn() + room.MimeDone = false + if err := saveRoom(room); err != nil { + log.Error("failed to save room", "error", err) + } + notify(models.NotifyRoomUpdatePrefix+room.ID, "") + notifyBotIfNeeded(room) + StopTurnTimer(roomID) + return + } + room.Settings.TurnSecondsLeft-- + if err := saveRoom(room); err != nil { + log.Error("failed to save room", "error", err) + } + notify(models.NotifyRoomUpdatePrefix+room.ID, "") + } + }() +} + +func StopTurnTimer(roomID string) { + mu.Lock() + defer mu.Unlock() + + if ticker, exists := timers[roomID]; exists { + ticker.Stop() + delete(timers, roomID) + } +} diff --git a/models/main.go b/models/main.go index 3d8580a..9346c3b 100644 --- a/models/main.go +++ b/models/main.go @@ -325,10 +325,10 @@ type WordCard struct { } type GameSettings struct { - Language string `json:"language" example:"en" form:"language"` - RoomPass string `json:"room_pass"` - TurnSeconds uint32 - RoundTime uint32 + Language string `json:"language" example:"en" form:"language"` + RoomPass string `json:"room_pass"` + TurnSecondsLeft uint32 + RoundTime uint32 } // =====