e450a6a2ed
Single source of truth for any agent/session continuing the project: run instructions (dev + Docker stack), architecture, the client<->server gamification sync rule, full feature status, CI/CD + Nexus HTTP-mirror cert workaround, gotchas, and the TODO list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.4 KiB
9.4 KiB
Barg-e Vasat (برگ وسط) — Project Handoff
Persian Hokm card game for the Iran market. Web/PWA + Android (Capacitor), vs-AI and online multiplayer, with a full gamification economy. This doc is the single source of truth for any agent/session continuing the project.
1. Repo, remotes, run
- Code root:
D:\Projects\hokm(Next.js app at root; .NET backend underserver/). - Git remote
origin= Gitea:https://git.soroushasadi.com/soroushdes/HokmPlay.git, branchmain. (No GitHub remote.) Pushingorigin maintriggers CI/CD (Gitea Actions). - Brand: name «برگ وسط» / "Barg-e Vasat" (insider Hokm slang — middle card when weak). Don't rename to "Hokm"; folder/repo stay hokm/HokmPlay.
Run locally (dev)
# frontend (root) — http://localhost:3000
npm run dev
# backend (.NET 10 + SignalR) — http://localhost:5005
cd server && HOKM_USE_SQLITE=1 dotnet run --project src/Hokm.Server/Hokm.Server.csproj
.env.local for live mode: NEXT_PUBLIC_USE_SERVER=1, NEXT_PUBLIC_SERVER_URL=http://localhost:5005.
Without NEXT_PUBLIC_USE_SERVER=1 the app runs fully offline against the in-memory mock service.
Run the Docker stack (local, prod-like) — what the user actually tests
Ports are in 1500–1600 so they coexist with npm run dev/dotnet run:
- web
http://localhost:1500(nginx serving the static export) → api:1505(.NET) →:1510postgres.
cd D:\Projects\hokm
copy deploy\ENV_FILE.example .env # set JWT_KEY, POSTGRES_PASSWORD; registries default to HTTP Nexus
docker compose build server web
docker compose up -d
Gotcha: after rebuilding the web image, docker compose up -d sometimes says "Running" and keeps the old image. Force it:
docker stop hokm-web && docker rm hokm-web && docker compose up -d --no-deps web
Build / verify (run before committing)
rm -rf .next && npx tsc --noEmit # types (rm .next first — stale .next/dev breaks typedRoutes)
npx tsx scripts/sim.ts # engine + gamification self-test (200 matches + 500 rewards)
dotnet build server/Hokm.slnx -c Release # server
npm run build # next static export
2. Architecture (the important bits)
- Frontend: Next 16 (App Router,
output:"export"→ static), React 19, Tailwind v4, Framer Motion, Zustand. RTL Persian default + English; custom i18n insrc/lib/i18n.tsx(NOT next-intl) — every string must be added to BOTH thefaandendicts. - Engine: pure-TS Hokm engine/AI in
src/lib/hokm/; mirrored as C# (server/src/Hokm.Engine, static classRules— notEngine, to avoid namespace clash). Validated byscripts/sim.ts(TS) andserver/tools/Hokm.Sim(C#). - Service seam: all networking goes through
OnlineService(src/lib/online/service.ts). Two impls:MockOnlineService(offline) andSignalrService(live).getService()picks viaNEXT_PUBLIC_USE_SERVER==="1". - Backend: .NET 10 ASP.NET Core + SignalR (
server/src/Hokm.Server), EF Core (SQLite dev / Npgsql Postgres prod), JWT. Hub/hub/game; REST under/api/*. Profile stored as a JSON blob (ProfileRow) + coinLedger. In-memory matchmaking/rooms inGameManager/GameRoom. - ⚠️ CRITICAL — keep gamification in sync:
src/lib/online/gamification.ts(client) andserver/src/Hokm.Server/Profiles/Gamification.cs(server) implement the SAME rules (rating/Elo, coins, XP, achievements, titles). In live mode the server is authoritative — ranked games run server-side and pushreward/profileover the hub; client-run (vs-computer/private) games submit aMatchSummarytoPOST /api/match/result. If you change a rule, change BOTH files identically (ids/goals/coins/metrics/formulas). - Zustand stores:
ui-store(History-API screen routing via hash),session-store(auth + profile, subscribes hubonProfile),online-store(friends/chat/leaderboard/matchmaking),game-store(the table driver —mode: ai|online,live, turn timer, minimize/resume, forfeit),sound-store,notification-store,celebration-store.
3. Feature status (DONE)
- Full offline vs-AI game (engine, AI, turn timer + auto-play, disconnect/reconnect sim).
- Online multiplayer over SignalR: matchmaking (pro skips queue, bots fill after wait), live server-run ranked games, server-authoritative entry/rewards.
- Economy: coins; ranked entry = stake (win +stake [+kot 40], lose −stake); free vs-computer/private rooms. Buy-coins via ZarinPal sandbox (merchant
299685fb-cadf-4dfc-98e2-d4af5d81528d, config-driven). Coin packs: starter 50k/95,000﷼, … Stores (Bazaar/Myket) must use their IAB (/api/coins/iab/verifyscaffolded; token verification TODO). - XP/levels: every game grants XP, winner ×2; premium (pro) ×1.5; max level 100; curve
100*lvl + 15*lvl². Store sells XP packs (xp1 +200/5k, xp2 +600/12k, xp3 +1500/25k coins; consumable; unlocks level achievements). - Achievements: ~100, metric-driven generator (categories: victory/kot/streak/hakem/level/rank/veteran), incl. "7× hakem", "7–0 sweep". Dedicated AchievementsScreen (tabbed) + Profile summary. Some unlock sticker packs.
- Cosmetics: avatars, titles (incl. expert/professional/captain/leader ladder), card front+back, reaction packs, sticker packs (custom SVG art incl. crown/seven-zip/streak-fire). Profile photo upload gated at level ≥ 25.
- Social: friends + chat server-persisted (
Social/SocialService, REST + hubfriendRequest/social/chat); friend remove needs confirm. Premium chat = animated gold bubbles. - Forfeit: request + teammate-confirm (server
GameRoomforfeit flow); penalty = lose 2× coins + 0 XP (NO kot, and never mention kot); confirm dialog alerts the penalty. - End-of-game roster:
MatchPlayersListon the final screen (reward modal + AI match-over) lists everyone; Add-friend button for real non-bot players (seatuserIdthreaded from server). - Celebrations:
celebration-store+CelebrationOverlay— animated XP count-up, level-up pop, achievement unlock; fires from shop purchases (XP/cosmetic) and unlocks. Reusable viacelebrate({...}). - UX/UI: "Persian luxury" palette (navy/teal/gold, glass) + UNO-style tactile UX rolled out to Home (hero Play), Shop (detail sheets), Lobby, Matchmaking, Profile, Leaderboard. Primitives in
globals.css:.press-3d(tactile press),.safe-top/.safe-bottom/.safe-x(notch),.hud-shadow,.premium-chat. Online count floored at ≥50. Match stays alive on exit (minimize/resume + ResumeGameBar). No fake/periodic notifications (removed as spam). - Capacitor Android APK builds (Myket maven mirror at root
https://maven.myket.ir; init script pins buildTools 36 + JDK17). SeeANDROID.md. - CI/CD (Gitea Actions + Nexus mirror) + Docker stack. See
DEPLOY.md.
4. CI/CD + mirrors (Iran constraints)
.gitea/workflows/ci-cd.yml: api-build (dotnet build slnx + Hokm.Sim), web-check (tsc + next build), deploy (self-hosted; pg_dump backup → rollback tag → build → stop+rm+up--no-deps→ health-wait → prune). Set theENV_FILErepo secret (seedeploy/ENV_FILE.example).- NuGet/npm go through the Nexus mirror over PLAIN HTTP
http://171.22.25.73:8081/repository/{nuget,npm}-group/— the HTTPS mirror serves a partial cert chain that container trust stores reject (NU1301 PartialChain / npm UNABLE_TO_GET_ISSUER). npm also uses--strict-ssl=false; NuGet HTTP source needsallowInsecureConnections="true". Local Windows dev + Docker base-image pulls work over HTTPS (Windows trust store has the intermediate) — only in-container package feeds use HTTP. - Fonts are self-hosted (
@fontsource-variable/vazirmatn+plus-jakarta-sans) —next/font/googlefetches Google at build time and FAILS on the Iran runner. Do not reintroducenext/font/google. - Memory: localhost can be VPN-hijacked (EonVPN) → reach local services via LAN IP if needed.
5. TODO / next
- Generate real EF migrations (
dotnet ef migrations add Init, DesignTimeDbContextFactory targets Postgres) + point at live Supabase; today the server usesEnsureCreated()(auto-switches toMigrate()once migrations exist). - Deeper game-table UNO restyle (bigger tactile cards, clearer turn/HUD, punchier win/trick feedback) — the last UI surface not yet refreshed.
- Store IAB token verification (Cafe Bazaar Poolakey / Myket) —
/api/coins/iab/verifyis a stub. - Iranian push provider for closed-app notifications (FCM/APNs blocked); in-app + real-time notifications already work.
- Optional: colored-chat visibility to OTHER players (needs sender-plan on chat messages); route daily-reward through the celebration overlay.
6. Working notes / gotchas
- Can't use the headless preview to verify visuals (it pauses animations/screenshots) — verify via builds + ask the user for screenshots. UI changes have been shipped "by the numbers".
- Server binds 0.0.0.0 in Docker via
ENTRYPOINT … --urls http://0.0.0.0:5005(appsettingsUrls=localhostwins over env, so command-line args are used). - After schema changes in SQLite dev with
EnsureCreated(), deleteserver/src/Hokm.Server/hokm.db*to recreate. - Background
dotnet run &from a Git-Bash shell dies when the shell exits; use a tracked background runner. - Commit messages end with
Co-Authored-By: Claude …. Bothmessages/-style i18n strings live insrc/lib/i18n.tsx(fa+en).