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.
141 lines
3.4 KiB
Go
141 lines
3.4 KiB
Go
package snake
|
|
|
|
import (
|
|
"context"
|
|
|
|
"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.
|
|
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 {
|
|
player := &Player{
|
|
Nickname: row.Nickname,
|
|
Slot: int(row.Slot),
|
|
}
|
|
|
|
if row.UserID != nil {
|
|
player.UserID = row.UserID
|
|
player.ID = PlayerID(*row.UserID)
|
|
} else if row.GuestPlayerID != nil {
|
|
player.ID = PlayerID(*row.GuestPlayerID)
|
|
}
|
|
|
|
players = append(players, player)
|
|
}
|
|
return players
|
|
}
|