Stage 4: lobby & social (matchmaking, friends, blocks, chat+nudge, invitations, profile, email, multi-player drop-out)
Engine: multi-player drop-out-and-continue with a per-game tile disposition (remove default / return), resigned seats skipped and excluded from the win, leaver rack never revealed; 2-player behaviour unchanged. New domains (service/store, no HTTP yet): internal/social (friend request/accept graph, per-user blocks, per-game chat with nudge as a message kind, content filter via mvdan.cc/xurls/v2 + leet/separator normaliser + phone heuristic) and internal/lobby (in-memory variant-keyed matchmaking pool, friend-game invitations invite->accept with lazy 7-day expiry). account gains profile editing and the email confirm-code flow (Mailer seam: SMTP or log mailer). Migration 00003_social.sql + regenerated jet. main wires the new services into the server (accessors for the Stage 6 handlers); robot substitution stays in Stage 5, REST/stream/push in Stage 6/8. Docs (PLAN, ARCHITECTURE, FUNCTIONAL+ru, TESTING, README) updated.
This commit is contained in:
@@ -37,6 +37,7 @@ type gameInsert struct {
|
||||
turnTimeoutSecs int
|
||||
hintsAllowed bool
|
||||
hintsPerPlayer int
|
||||
dropoutTiles string
|
||||
}
|
||||
|
||||
// statDelta is one account's contribution to its statistics on a game finish.
|
||||
@@ -91,7 +92,8 @@ func (s *Store) CreateGame(ctx context.Context, ins gameInsert, seats []uuid.UUI
|
||||
gi := table.Games.INSERT(
|
||||
table.Games.GameID, table.Games.Variant, table.Games.DictVersion, table.Games.Seed,
|
||||
table.Games.Players, table.Games.TurnTimeoutSecs, table.Games.HintsAllowed, table.Games.HintsPerPlayer,
|
||||
).VALUES(ins.id, ins.variant, ins.dictVersion, ins.seed, ins.players, ins.turnTimeoutSecs, ins.hintsAllowed, ins.hintsPerPlayer)
|
||||
table.Games.DropoutTiles,
|
||||
).VALUES(ins.id, ins.variant, ins.dictVersion, ins.seed, ins.players, ins.turnTimeoutSecs, ins.hintsAllowed, ins.hintsPerPlayer, ins.dropoutTiles)
|
||||
if _, err := gi.ExecContext(ctx, tx); err != nil {
|
||||
return fmt.Errorf("insert game: %w", err)
|
||||
}
|
||||
@@ -367,6 +369,10 @@ func projectGame(g model.Games, seats []model.GamePlayers) (Game, error) {
|
||||
if err != nil {
|
||||
return Game{}, fmt.Errorf("game: %s: %w", g.GameID, err)
|
||||
}
|
||||
dropout, err := engine.ParseDropoutTiles(g.DropoutTiles)
|
||||
if err != nil {
|
||||
return Game{}, fmt.Errorf("game: %s: %w", g.GameID, err)
|
||||
}
|
||||
out := Game{
|
||||
ID: g.GameID,
|
||||
Variant: variant,
|
||||
@@ -378,6 +384,7 @@ func projectGame(g model.Games, seats []model.GamePlayers) (Game, error) {
|
||||
TurnTimeout: time.Duration(g.TurnTimeoutSecs) * time.Second,
|
||||
HintsAllowed: g.HintsAllowed,
|
||||
HintsPerPlayer: int(g.HintsPerPlayer),
|
||||
DropoutTiles: dropout,
|
||||
MoveCount: int(g.MoveCount),
|
||||
CreatedAt: g.CreatedAt,
|
||||
UpdatedAt: g.UpdatedAt,
|
||||
|
||||
Reference in New Issue
Block a user