Commit Graph

42 Commits

Author SHA1 Message Date
Ryan Hamamura
9a8fe4534d feat: add hashfs for static asset cache busting and live clock
All checks were successful
CI / Deploy / test (pull_request) Successful in 32s
CI / Deploy / lint (pull_request) Successful in 42s
CI / Deploy / deploy (pull_request) Has been skipped
- Add assets package with dev/prod build tags
- Dev: serve from filesystem with Cache-Control: no-store
- Prod: use hashfs for cache-busting URLs
- Add LiveClock component to show SSE connection status
- Update templates to use StaticPath for asset URLs
2026-03-03 13:26:52 -10:00
Ryan Hamamura
b2b06a062b fix: align SSE architecture with portigo for reliable connections
All checks were successful
CI / Deploy / test (pull_request) Successful in 35s
CI / Deploy / lint (pull_request) Successful in 45s
CI / Deploy / deploy (pull_request) Has been skipped
- Reorder HandleGameEvents to create NATS subscriptions before SSE
- Use chi's middleware.NewWrapResponseWriter for proper http.Flusher support
- Add slog-zerolog adapter for unified logging
- Add ErrorLog to HTTP server for better error visibility
- Change session Cookie.Secure to false for HTTP support
- Change heartbeat from 15s to 10s
- Remove ConnectionIndicator patching (was causing PatchElementsNoTargetsFound)

The key fix was using chi's response writer wrapper which properly
implements http.Flusher, allowing SSE data to be flushed immediately
instead of being buffered.
2026-03-03 11:57:58 -10:00
Ryan Hamamura
eb75654403 feat: display app version in UI footer
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
- Add version package with build-time variables
- Inject version via ldflags in Dockerfile using git describe
- Show version in footer on every page
- Log version and commit on server startup
2026-03-03 09:40:23 -10:00
Ryan Hamamura
fb6c0e3d90 refactor: replace hardcoded NATS subjects with typed helpers
Some checks failed
CI / Deploy / test (pull_request) Successful in 14s
CI / Deploy / lint (pull_request) Failing after 21s
CI / Deploy / deploy (pull_request) Has been skipped
Add GameSubject/ChatSubject helpers to connect4 and snake packages,
eliminating magic string concatenation from handlers and main.go.
2026-03-02 21:30:47 -10:00
Ryan Hamamura
c6885a069b refactor: rename Go module from c4 to games
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
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
Ryan Hamamura
38eb9ee398 refactor: rename game package to connect4, drop Game prefix from types
All checks were successful
CI / Deploy / test (pull_request) Successful in 16s
CI / Deploy / lint (pull_request) Successful in 25s
CI / Deploy / deploy (pull_request) Has been skipped
Rename game/ -> connect4/ to avoid c4/game stutter. Drop redundant
Game prefix from exported types (GameStore -> Store, GameInstance ->
Instance, GameStatus -> Status). Rename NATS subjects from game.{id}
to connect4.{id}. URL routes unchanged.
2026-03-02 20:31:00 -10:00
Ryan Hamamura
587f392b8b fix: serve datastar locally and clean up session/route config
Replace CDN-hosted datastar beta.11 with local v1.0.0-RC.7 to fix
client-side expression incompatibilities with the Go SDK. Also fix
quoted CSS class keys in data-class expressions, harden session cookie
settings (named cookie, Secure flag), simplify SetupRoutes to not
return an error, and regenerate templ output.
2026-03-02 14:34:39 -10:00
Ryan Hamamura
2aa026b1d5 refactor: remove persister abstraction layer
Some checks failed
CI / Deploy / test (pull_request) Successful in 8s
CI / Deploy / lint (pull_request) Failing after 46s
CI / Deploy / deploy (pull_request) Has been skipped
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
2026-03-02 12:30:33 -10:00
Ryan Hamamura
8c3b3fc6ea refactor: replace via framework with chi + templ + datastar
Some checks failed
CI / Deploy / test (pull_request) Successful in 28s
CI / Deploy / lint (pull_request) Failing after 42s
CI / Deploy / deploy (pull_request) Has been skipped
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
2026-03-02 12:16:25 -10:00
Ryan Hamamura
2df20c2840 refactor: adopt portigo infrastructure patterns
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.
2026-03-02 11:48:47 -10:00
Ryan Hamamura
e68e4b48f5 fix: resolve nil pubsub preventing live game updates
All checks were successful
Deploy c4 / deploy (push) Successful in 45s
v.PubSub() was captured at startup before v.Start() initialized NATS,
so both stores held nil and notify() silently no-oped. Replace the
PubSub interface with a callback that evaluates v.PubSub() lazily at
call time.
2026-02-20 12:37:28 -10:00
Ryan Hamamura
91b5f2b80c deps: update ryanhamamura/via to v0.23.0
All checks were successful
Deploy c4 / deploy (push) Successful in 57s
Remove ContextSuspendAfter and ContextTTL options, which were
deleted upstream. Contexts now persist until SSE close beacon
or server shutdown.
2026-02-20 12:06:51 -10:00
Ryan Hamamura
884650c68d feat: integrate via v0.18.1 context suspension and key throttling
Some checks failed
Deploy c4 / deploy (push) Has been cancelled
Upgrade via to v0.18.1 and configure context suspension timeouts
(5min suspend, 30min TTL) for clean reconnection behavior. Throttle
snake direction key inputs to 100ms to prevent wasted SSE round-trips
when keys are held down.
2026-02-19 11:14:35 -10:00
3593197271 feat: persist chat messages to SQLite (#1)
All checks were successful
Deploy c4 / deploy (push) Successful in 44s
2026-02-13 22:00:01 +00:00
Ryan Hamamura
deff9b3859 fix: renew session token after login/register to persist session data
All checks were successful
Deploy c4 / deploy (push) Successful in 42s
Without RenewToken(), session data set during the action handler
wasn't surviving the redirect — the old pre-auth token was stale.
2026-02-13 11:35:37 -10:00
Ryan Hamamura
9069530e47 feat: add in-game chat to Connect 4
All checks were successful
Deploy c4 / deploy (push) Successful in 43s
Add real-time chat alongside the game board, mirroring the snake chat
implementation. Fix mobile layout for both C4 and snake chats — expand
chat to full width and reduce history height on small screens.
2026-02-13 10:54:19 -10:00
Ryan Hamamura
e85271ab29 feat: stealth mode — replace game-related text with discrete symbols
All checks were successful
Deploy c4 / deploy (push) Successful in 40s
Replace "Game Lobby", "Connect 4", and "Snake" headings with colored
circles (●●●●) and tildes (~~~~) so the UI is less obviously a game
at a glance. Also neutralize the lobby description text and shorten
the back link.
2026-02-13 09:22:07 -10:00
Ryan Hamamura
427521505b feat: add Docker Compose deployment and serve assets via StaticFS
Embed full assets directory and serve with via's StaticFS instead of a
custom HTTP handler. Move SQLite DB to data/c4.db for clean volume
mounting. Add multi-stage Dockerfile, docker-compose.yml, and
.dockerignore.
2026-02-13 08:02:15 -10:00
Ryan Hamamura
d2ed3cffd9 deps: update via to v0.15.0, remove vianats dependency
NATS is now built into via.New() automatically. Stores use the new
v.PubSub() accessor instead of the removed vianats package.
2026-02-12 15:10:18 -10:00
Ryan Hamamura
3d019fd948 feat: add snake multiplayer chat as sidebar with vivid player colors
Move chat to the right of the board using a flex wrapper that stacks
vertically on narrow screens. Restore original snake player colors by
removing the desaturation filter added with the dark theme.
2026-02-05 10:15:26 -10:00
Ryan Hamamura
0279615b36 feat: add back to lobby navigation on game pages 2026-02-05 09:55:43 -10:00
Ryan Hamamura
73128dc119 style: dark stealth theme with teal/burgundy pieces and cache-busting
Darken the DaisyUI theme and game board colors for a muted, low-chroma
aesthetic. Pieces use dark teal vs burgundy for subtle contrast.
Add MD5-based cache busting to the DaisyUI stylesheet link so CSS
changes are picked up without a hard refresh.
2026-02-05 09:51:36 -10:00
Ryan Hamamura
e239e948ae feat: add configurable speed and expanded grid presets for snake
- Add per-game speed setting with presets (Slow/Normal/Fast/Insane)
- Add speed selector UI in snake lobby
- Expand grid presets with Tiny (15x15) and XL (50x30)
- Auto-calculate cell size based on grid dimensions
- Preserve speed setting in rematch games
2026-02-04 10:02:40 -10:00
Ryan Hamamura
f454e0d220 feat: add single player snake mode
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
2026-02-04 07:33:02 -10:00
Ryan Hamamura
7faf94fa6d feat: make invite link base URL configurable via APP_URL
Load environment variables from .env file using godotenv.
Defaults to https://demo.adriatica.io if APP_URL is not set.
2026-02-04 07:02:52 -10:00
Ryan Hamamura
2dc75107d1 Add visual smoothing for snake game and systemd deployment
CSS animations for smoother 60fps feel despite 7Hz game ticks:
- 130ms transitions on cell background/box-shadow
- Head pop-in animation on direction changes
- Food pulse animation
- Smooth death state fade with grayscale

Per-snake colored glow on head cells.

Make server port configurable via PORT env var (default 8080).

Add deploy/ with systemd service and scripts:
- setup.sh: create games user, /opt/c4, install unit
- deploy.sh: build and install binary, restart service
- package.sh: cross-compile, tarball, base64 split for transfer
- reassemble.sh: decode and extract on target server
2026-02-04 06:50:18 -10:00
Ryan Hamamura
038c4b3f22 Update via to v0.4.0 and decouple tick rate from snake speed
Use via.OnKeyDownMap for snake keybindings, replacing the manual
dataExpr/rawDataAttr workaround. Window-scoped key handling removes
the need for tabindex/focus hacks, and WithPreventDefault on arrow
keys prevents page scrolling during gameplay.

Introduce a 60 FPS tick loop with a separate snake movement speed
(7 cells/s) so direction input is polled every frame but game state
only advances at the configured rate.
2026-02-02 09:18:13 -10:00
Ryan Hamamura
7e78664534 WIP: Add multiplayer Snake game
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).
2026-02-02 07:26:28 -10:00
Ryan Hamamura
a6b5a46a8a Replace polling loop with NATS pub/sub for game updates
Use via's embedded NATS server to notify players of state changes
instead of a 100ms polling ticker. Each player subscribes to
"game.<id>" on page load; via auto-cleans subscriptions on disconnect,
eliminating the need for manual player tracking and RegisterSync.
2026-01-31 10:26:31 -10:00
Ryan Hamamura
d079b90a33 Update via dependency from v0.2.5 to v0.3.1 2026-01-31 09:30:32 -10:00
Ryan Hamamura
f590a2444a Replace PicoCSS with DaisyUI + Tailwind v4
Use gotailwind (standalone Tailwind v4 via Go tool) with DaisyUI
plugin files — no npm needed. CSS is compiled at build time and
embedded via a Via Plugin that serves it as a static file.

Custom "connect4" theme: light, warm, playful palette with red/yellow
accents matching game pieces and board blue accent.
2026-01-31 07:31:29 -10:00
Ryan Hamamura
dcab4343e5 Add login prompt for game invite links
When unauthenticated users visit a game invite link, show a prompt
with options to log in, register, or continue as guest. After
login/register, redirect back to the original game URL.
2026-01-22 21:50:50 -10:00
Ryan Hamamura
5e1712c4a0 Use SQLite for session storage
Upgrade via to v0.2.5 and switch from in-memory sessions to SQLite-backed
sessions for persistence across server restarts.
2026-01-22 16:53:47 -10:00
Ryan Hamamura
ee762fb841 Fix player reconnection by re-registering sync context
When an existing player reconnects to a game, their sync context
is now updated so they continue receiving real-time updates.
2026-01-14 21:54:07 -10:00
Ryan Hamamura
2cd5b1d289 Add play again button for rematch after game ends
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.
2026-01-14 18:02:26 -10:00
Ryan Hamamura
5f452914f8 Add game deletion with authorization check
- 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
2026-01-14 17:44:09 -10:00
Ryan Hamamura
d96f7dcc29 Show user's active games on home page after login 2026-01-14 17:11:27 -10:00
Ryan Hamamura
b264d8990b Add user authentication and game persistence with SQLite
- 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)
2026-01-14 16:59:40 -10:00
Ryan Hamamura
03dcfdbf85 Revert "Auto-generate nicknames for invitees joining via link"
This reverts commit 32e4e61635.
2026-01-14 15:41:43 -10:00
Ryan Hamamura
32e4e61635 Auto-generate nicknames for invitees joining via link
Invitees no longer need to enter a nickname - they automatically
join with a random name like "Swift Tiger" or "Happy Falcon".
Game creators still enter their nickname manually.
2026-01-14 14:45:04 -10:00
Ryan Hamamura
63d0773ab5 Simplify codebase and fix Enter key on home page
- Enter key now triggers createGame action on home page
- Remove redundant setNickname action from home page
- Remove unused code: join channel, Leave(), Stop() methods
- Consolidate ID generation into game.GenerateID()
- Remove unused CreatedAt field from Game struct
2026-01-14 14:10:18 -10:00
Ryan Hamamura
389fc12bf2 Add Connect 4 multiplayer game server
Real-time two-player Connect 4 using Via framework with:
- Game creation and invite links
- SSE-based live updates for both players
- Win detection with animated highlighting
- Session-based nickname persistence
2026-01-14 12:57:57 -10:00