refactor: add save()/savePlayer() methods on game instances
All checks were successful
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped

Wrap free persistence functions in instance methods for cleaner call
sites (gi.save() instead of saveGame(gi.queries, gi.game)). Methods
log errors via zerolog before returning them.
This commit is contained in:
Ryan Hamamura
2026-03-02 18:51:18 -10:00
parent cb5458c9fc
commit 9a20467438
5 changed files with 48 additions and 12 deletions

View File

@@ -4,8 +4,26 @@ import (
"context" "context"
"github.com/ryanhamamura/c4/db/repository" "github.com/ryanhamamura/c4/db/repository"
"github.com/rs/zerolog/log"
) )
func (gi *GameInstance) save() error {
err := saveGame(gi.queries, gi.game)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Msg("failed to save game")
}
return err
}
func (gi *GameInstance) savePlayer(player *Player, slot int) error {
err := saveGamePlayer(gi.queries, gi.game.ID, player, slot)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Int("slot", slot).Msg("failed to save game player")
}
return err
}
// saveGame persists the game state via upsert. // saveGame persists the game state via upsert.
func saveGame(queries *repository.Queries, g *Game) error { func saveGame(queries *repository.Queries, g *Game) error {
var winnerUserID *string var winnerUserID *string

View File

@@ -49,7 +49,7 @@ func (gs *GameStore) Create() *GameInstance {
gs.gamesMu.Unlock() gs.gamesMu.Unlock()
if gs.queries != nil { if gs.queries != nil {
saveGame(gs.queries, gi.game) //nolint:errcheck gi.save() //nolint:errcheck
} }
return gi return gi
@@ -152,8 +152,8 @@ func (gi *GameInstance) Join(ps *PlayerSession) bool {
} }
if gi.queries != nil { if gi.queries != nil {
saveGamePlayer(gi.queries, gi.game.ID, ps.Player, slot) //nolint:errcheck gi.savePlayer(ps.Player, slot) //nolint:errcheck
saveGame(gi.queries, gi.game) //nolint:errcheck gi.save() //nolint:errcheck
} }
gi.notify() gi.notify()
@@ -190,7 +190,7 @@ func (gi *GameInstance) CreateRematch(gs *GameStore) *GameInstance {
gi.game.RematchGameID = &newID gi.game.RematchGameID = &newID
if gi.queries != nil { if gi.queries != nil {
if err := saveGame(gi.queries, gi.game); err != nil { if err := gi.save(); err != nil {
gs.Delete(newID) //nolint:errcheck gs.Delete(newID) //nolint:errcheck
gi.game.RematchGameID = nil gi.game.RematchGameID = nil
return nil return nil
@@ -224,7 +224,7 @@ func (gi *GameInstance) DropPiece(col int, playerColor int) bool {
} }
if gi.queries != nil { if gi.queries != nil {
saveGame(gi.queries, gi.game) //nolint:errcheck gi.save() //nolint:errcheck
} }
gi.notify() gi.notify()

View File

@@ -62,7 +62,7 @@ func (si *SnakeGameInstance) countdownPhase() {
si.game.Status = StatusInProgress si.game.Status = StatusInProgress
if si.queries != nil { if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck si.save() //nolint:errcheck
} }
si.gameMu.Unlock() si.gameMu.Unlock()
si.notify() si.notify()
@@ -123,7 +123,7 @@ func (si *SnakeGameInstance) gamePhase() {
if time.Since(lastInput) > inactivityLimit { if time.Since(lastInput) > inactivityLimit {
si.game.Status = StatusFinished si.game.Status = StatusFinished
if si.queries != nil { if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck si.save() //nolint:errcheck
} }
si.gameMu.Unlock() si.gameMu.Unlock()
si.notify() si.notify()
@@ -193,7 +193,7 @@ func (si *SnakeGameInstance) gamePhase() {
// Throttle DB saves: persist on game over or every 2 seconds // Throttle DB saves: persist on game over or every 2 seconds
if si.queries != nil && (gameOver || time.Since(lastSave) >= 2*time.Second) { if si.queries != nil && (gameOver || time.Since(lastSave) >= 2*time.Second) {
saveSnakeGame(si.queries, si.game) //nolint:errcheck si.save() //nolint:errcheck
lastSave = time.Now() lastSave = time.Now()
} }

View File

@@ -4,8 +4,26 @@ import (
"context" "context"
"github.com/ryanhamamura/c4/db/repository" "github.com/ryanhamamura/c4/db/repository"
"github.com/rs/zerolog/log"
) )
func (si *SnakeGameInstance) save() error {
err := saveSnakeGame(si.queries, si.game)
if err != nil {
log.Error().Err(err).Str("game_id", si.game.ID).Msg("failed to save snake game")
}
return err
}
func (si *SnakeGameInstance) savePlayer(player *Player) error {
err := saveSnakePlayer(si.queries, si.game.ID, player)
if err != nil {
log.Error().Err(err).Str("game_id", si.game.ID).Msg("failed to save snake player")
}
return err
}
// saveSnakeGame persists the snake game state via upsert. // saveSnakeGame persists the snake game state via upsert.
func saveSnakeGame(queries *repository.Queries, sg *SnakeGame) error { func saveSnakeGame(queries *repository.Queries, sg *SnakeGame) error {
boardJSON := "{}" boardJSON := "{}"

View File

@@ -62,7 +62,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
ss.gamesMu.Unlock() ss.gamesMu.Unlock()
if ss.queries != nil { if ss.queries != nil {
saveSnakeGame(ss.queries, sg) //nolint:errcheck si.save() //nolint:errcheck
} }
return si return si
@@ -206,8 +206,8 @@ func (si *SnakeGameInstance) Join(player *Player) bool {
si.game.Players[slot] = player si.game.Players[slot] = player
if si.queries != nil { if si.queries != nil {
saveSnakePlayer(si.queries, si.game.ID, player) //nolint:errcheck si.savePlayer(player) //nolint:errcheck
saveSnakeGame(si.queries, si.game) //nolint:errcheck si.save() //nolint:errcheck
} }
si.notify() si.notify()
@@ -293,7 +293,7 @@ func (si *SnakeGameInstance) CreateRematch() *SnakeGameInstance {
si.game.RematchGameID = &newID si.game.RematchGameID = &newID
if si.queries != nil { if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck si.save() //nolint:errcheck
} }
si.gameMu.Unlock() si.gameMu.Unlock()