refactor: deduplicate persistence, add upsert queries, throttle snake saves
Some checks failed
CI / Deploy / test (pull_request) Failing after 15s
CI / Deploy / lint (pull_request) Failing after 23s
CI / Deploy / deploy (pull_request) Has been skipped

- Replace Create+Get+Update with UpsertGame/UpsertSnakeGame queries
- Extract free functions (saveGame, loadGame, etc.) from duplicated
  receiver methods on Store and Instance types
- Remove duplicate generateID from snake package, reuse game.GenerateID
- Throttle snake game DB writes to every 2s instead of every tick
- Fix double-lock in c4game chat handler
- Update all code for sqlc pointer types (*string instead of sql.NullString)
This commit is contained in:
Ryan Hamamura
2026-03-02 16:56:29 -10:00
parent 9c3f659e96
commit bc6488f063
14 changed files with 318 additions and 494 deletions

View File

@@ -2,11 +2,10 @@ package snake
import (
"context"
"crypto/rand"
"encoding/hex"
"sync"
"github.com/ryanhamamura/c4/db/repository"
"github.com/ryanhamamura/c4/game"
)
type SnakeStore struct {
@@ -39,7 +38,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
if speed <= 0 {
speed = DefaultSpeed
}
id := generateID(4)
id := game.GenerateID(4)
sg := &SnakeGame{
ID: id,
State: &GameState{
@@ -63,7 +62,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
ss.gamesMu.Unlock()
if ss.queries != nil {
ss.saveSnakeGame(sg) //nolint:errcheck
saveSnakeGame(ss.queries, sg) //nolint:errcheck
}
return si
@@ -82,12 +81,12 @@ func (ss *SnakeStore) Get(id string) (*SnakeGameInstance, bool) {
return nil, false
}
sg, err := ss.loadSnakeGame(id)
sg, err := loadSnakeGame(ss.queries, id)
if err != nil || sg == nil {
return nil, false
}
players, _ := ss.loadSnakePlayers(id)
players, _ := loadSnakePlayers(ss.queries, id)
if sg.Players == nil {
sg.Players = make([]*Player, 8)
}
@@ -207,8 +206,8 @@ func (si *SnakeGameInstance) Join(player *Player) bool {
si.game.Players[slot] = player
if si.queries != nil {
si.saveSnakePlayer(si.game.ID, player) //nolint:errcheck
si.saveSnakeGame(si.game) //nolint:errcheck
saveSnakePlayer(si.queries, si.game.ID, player) //nolint:errcheck
saveSnakeGame(si.queries, si.game) //nolint:errcheck
}
si.notify()
@@ -294,16 +293,10 @@ func (si *SnakeGameInstance) CreateRematch() *SnakeGameInstance {
si.game.RematchGameID = &newID
if si.queries != nil {
si.saveSnakeGame(si.game) //nolint:errcheck
saveSnakeGame(si.queries, si.game) //nolint:errcheck
}
si.gameMu.Unlock()
si.notify()
return newSI
}
func generateID(size int) string {
b := make([]byte, size)
_, _ = rand.Read(b)
return hex.EncodeToString(b)
}