- 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)
The sqlite3store library expects the sessions table to exist but does
not create it. On fresh deployments, the session store would fail.
Uses IF NOT EXISTS to be safe on databases where the table was created
outside of goose.
Inline persistence logic directly into game stores and handlers:
- game/persist.go: DB mapping methods on GameStore and GameInstance
- snake/persist.go: DB mapping methods on SnakeStore and SnakeGameInstance
- Chat persistence inlined into c4game handlers
- Delete db/persister.go (GamePersister, SnakePersister, ChatPersister)
- Stores now take *repository.Queries directly instead of Persister interface
Migrate from the via meta-framework to direct dependencies:
- chi for routing, templ for HTML templates, datastar for SSE/reactivity
- Feature-sliced architecture (features/{auth,lobby,c4game,snakegame}/)
- Shared layouts and components (features/common/)
- Handler factory pattern (HandleX(deps) http.HandlerFunc)
- Embedded NATS server (nats/), SCS sessions (sessions/), chi router wiring (router/)
- Move ChatMessage domain type from ui package to game package
- Remove old ui/ package (gomponents-based via/h views)
- Remove via dependency from go.mod entirely
Add config package with build-tag-switched dev/prod environments,
structured logging via zerolog, Taskfile for dev workflow, golangci-lint
config, testutil package, and improved DB setup with proper SQLite
pragmas and cleanup. Rename sqlc output package from gen to repository.
Switch to allowlist .gitignore, Alpine+UPX+scratch Dockerfile, and
CI pipeline with test/lint gates before deploy.
Add solo mode where players survive as long as possible while tracking
score (food eaten). Single player games start with a shorter 3-second
countdown vs 10 seconds for multiplayer, maintain exactly 1 food item
for classic snake feel, and end when the player dies rather than when
one player remains.
- Add GameMode type (ModeMultiplayer/ModeSinglePlayer) and Score field
- Filter single player games from "Join a Game" lobby list
- Show "Ready?" and "Score: X" UI for single player mode
- Hide invite link for single player games
- Preserve game mode on rematch
N-player (2-8) real-time Snake game alongside Connect 4.
Lobby has tabs to switch between games. Players join via
invite link with 10-second countdown. Game loop runs at
tick-based intervals with NATS pub/sub for state sync.
Keyboard input not yet working (Datastar keydown binding
issue still under investigation).
When a game finishes (win or draw), players see a "Play again" button.
Clicking it creates a new game and the opponent sees a "Join Rematch"
link to join the same game.
- Add Delete method to GameStore and Persister interface
- Add delete button to game list on home page
- Verify user owns game before allowing deletion
- Use status constants instead of magic numbers
- Remove unused variable in persister
- User registration/login with bcrypt password hashing
- SQLite database with goose migrations and sqlc-generated queries
- Games and players persisted to database, resumable after restart
- Guest play still supported alongside authenticated users
- Auth UI components (login/register forms, auth header, guest banner)