Files
games/snake/persist.go
Ryan Hamamura c6885a069b
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
refactor: rename Go module from c4 to games
Rename module path github.com/ryanhamamura/c4 to
github.com/ryanhamamura/games across go.mod, all source files,
and golangci config.
2026-03-02 20:41:20 -10:00

142 lines
3.4 KiB
Go

package snake
import (
"context"
"github.com/ryanhamamura/games/db/repository"
"github.com/ryanhamamura/games/player"
"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.
func saveSnakeGame(queries *repository.Queries, sg *SnakeGame) error {
boardJSON := "{}"
var gridWidth, gridHeight *int64
if sg.State != nil {
boardJSON = sg.State.ToJSON()
w, h := int64(sg.State.Width), int64(sg.State.Height)
gridWidth, gridHeight = &w, &h
}
var winnerUserID *string
if sg.Winner != nil && sg.Winner.UserID != nil {
winnerUserID = sg.Winner.UserID
}
return queries.UpsertSnakeGame(context.Background(), repository.UpsertSnakeGameParams{
ID: sg.ID,
Board: boardJSON,
Status: int64(sg.Status),
GridWidth: gridWidth,
GridHeight: gridHeight,
GameMode: int64(sg.Mode),
SnakeSpeed: int64(sg.Speed),
WinnerUserID: winnerUserID,
RematchGameID: sg.RematchGameID,
Score: int64(sg.Score),
})
}
func saveSnakePlayer(queries *repository.Queries, gameID string, player *Player) error {
var userID, guestPlayerID *string
if player.UserID != nil {
userID = player.UserID
} else {
id := string(player.ID)
guestPlayerID = &id
}
return queries.CreateSnakePlayer(context.Background(), repository.CreateSnakePlayerParams{
GameID: gameID,
UserID: userID,
GuestPlayerID: guestPlayerID,
Nickname: player.Nickname,
Color: int64(player.Slot + 1),
Slot: int64(player.Slot),
})
}
func loadSnakeGame(queries *repository.Queries, id string) (*SnakeGame, error) {
row, err := queries.GetSnakeGame(context.Background(), id)
if err != nil {
return nil, err
}
return snakeGameFromRow(row)
}
func loadSnakePlayers(queries *repository.Queries, id string) ([]*Player, error) {
rows, err := queries.GetSnakePlayers(context.Background(), id)
if err != nil {
return nil, err
}
return snakePlayersFromRows(rows), nil
}
// Domain ↔ DB mapping helpers.
func snakeGameFromRow(row *repository.Game) (*SnakeGame, error) {
state, err := GameStateFromJSON(row.Board)
if err != nil {
state = &GameState{}
}
if row.GridWidth != nil {
state.Width = int(*row.GridWidth)
}
if row.GridHeight != nil {
state.Height = int(*row.GridHeight)
}
sg := &SnakeGame{
ID: row.ID,
State: state,
Players: make([]*Player, 8),
Status: Status(row.Status),
Mode: GameMode(row.GameMode),
Score: int(row.Score),
Speed: int(row.SnakeSpeed),
}
if row.RematchGameID != nil {
sg.RematchGameID = row.RematchGameID
}
return sg, nil
}
func snakePlayersFromRows(rows []*repository.GamePlayer) []*Player {
players := make([]*Player, 0, len(rows))
for _, row := range rows {
p := &Player{
Nickname: row.Nickname,
Slot: int(row.Slot),
}
if row.UserID != nil {
p.UserID = row.UserID
p.ID = player.ID(*row.UserID)
} else if row.GuestPlayerID != nil {
p.ID = player.ID(*row.GuestPlayerID)
}
players = append(players, p)
}
return players
}