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.
128 lines
3.0 KiB
Go
128 lines
3.0 KiB
Go
package game
|
|
|
|
import (
|
|
"context"
|
|
|
|
"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.
|
|
func saveGame(queries *repository.Queries, g *Game) error {
|
|
var winnerUserID *string
|
|
if g.Winner != nil && g.Winner.UserID != nil {
|
|
winnerUserID = g.Winner.UserID
|
|
}
|
|
|
|
var winningCells *string
|
|
if wc := g.WinningCellsToJSON(); wc != "" {
|
|
winningCells = &wc
|
|
}
|
|
|
|
return queries.UpsertGame(context.Background(), repository.UpsertGameParams{
|
|
ID: g.ID,
|
|
Board: g.BoardToJSON(),
|
|
CurrentTurn: int64(g.CurrentTurn),
|
|
Status: int64(g.Status),
|
|
WinnerUserID: winnerUserID,
|
|
WinningCells: winningCells,
|
|
RematchGameID: g.RematchGameID,
|
|
})
|
|
}
|
|
|
|
func saveGamePlayer(queries *repository.Queries, gameID string, player *Player, slot int) error {
|
|
var userID, guestPlayerID *string
|
|
if player.UserID != nil {
|
|
userID = player.UserID
|
|
} else {
|
|
id := string(player.ID)
|
|
guestPlayerID = &id
|
|
}
|
|
|
|
return queries.CreateGamePlayer(context.Background(), repository.CreateGamePlayerParams{
|
|
GameID: gameID,
|
|
UserID: userID,
|
|
GuestPlayerID: guestPlayerID,
|
|
Nickname: player.Nickname,
|
|
Color: int64(player.Color),
|
|
Slot: int64(slot),
|
|
})
|
|
}
|
|
|
|
func loadGame(queries *repository.Queries, id string) (*Game, error) {
|
|
row, err := queries.GetGame(context.Background(), id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return gameFromRow(row)
|
|
}
|
|
|
|
func loadGamePlayers(queries *repository.Queries, id string) ([]*Player, error) {
|
|
rows, err := queries.GetGamePlayers(context.Background(), id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return playersFromRows(rows), nil
|
|
}
|
|
|
|
// Domain ↔ DB mapping helpers.
|
|
|
|
func gameFromRow(row *repository.Game) (*Game, error) {
|
|
g := &Game{
|
|
ID: row.ID,
|
|
CurrentTurn: int(row.CurrentTurn),
|
|
Status: GameStatus(row.Status),
|
|
}
|
|
|
|
if err := g.BoardFromJSON(row.Board); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if row.WinningCells != nil {
|
|
_ = g.WinningCellsFromJSON(*row.WinningCells)
|
|
}
|
|
|
|
if row.RematchGameID != nil {
|
|
g.RematchGameID = row.RematchGameID
|
|
}
|
|
|
|
return g, nil
|
|
}
|
|
|
|
func playersFromRows(rows []*repository.GamePlayer) []*Player {
|
|
players := make([]*Player, 0, len(rows))
|
|
for _, row := range rows {
|
|
player := &Player{
|
|
Nickname: row.Nickname,
|
|
Color: int(row.Color),
|
|
}
|
|
|
|
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
|
|
}
|