Files
HokmPlay/server
soroush.asadi e778e8b5bd Server-backed friends, chat, IAB scaffold + EF migrations/Postgres
- Social: EF-backed friends graph + chat (SocialService/SocialModels);
  REST endpoints (friends add/accept/decline/remove/list/requests,
  chat conversations/messages/send) with real-time hub events
  (friendRequest/social/chat). GameManager tracks online users for presence.
- Client SignalrService: friends + chat now hit the server and react to
  hub events (refetch + emit); no longer delegated to the mock.
- IAB: /api/coins/iab/verify endpoint + IabVerifyReq for Cafe Bazaar/Myket
  (token verification is a documented TODO pending store accounts/SKUs).
- Persistence: EF Core Design package + DesignTimeDbContextFactory (Postgres),
  Program auto-migrate/EnsureCreated, appsettings.Production.json.example
  with Supabase connection + live ZarinPal template.

Verified end-to-end (two users, SQLite dev): request -> accept ->
bidirectional friends, chat send with per-user fromMe, unread count +
read-on-fetch. Server + client builds clean (dotnet build, tsc, next build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:26:22 +03:30
..

Hokm — .NET 10 + SignalR backend

Authoritative realtime server for the Hokm game. The TypeScript engine (../src/lib/hokm) is ported to C# here so the server is the source of truth.

Projects

Project What
src/Hokm.Engine Pure C# rules + AI (port of src/lib/hokm) — deal, hakem, trump, tricks, scoring, Kot, bot
src/Hokm.Server ASP.NET Core + SignalR GameHub, in-memory matchmaking/rooms, JWT auth
tools/Hokm.Sim All-AI simulation that validates the engine port

NuGet sources

NuGet.config restores only from the configured mirrors (no nuget.org):

  • https://mirror.soroushasadi.com/repository/nuget-group/index.json (Nexus)
  • https://package-mirror.liara.ir/repository/nuget/index.json (Liara)

Directory.Build.props disables NuGet audit (avoids reaching api.nuget.org).

Run

cd server
dotnet run --project tools/Hokm.Sim -c Release   # validate the engine
dotnet run --project src/Hokm.Server -c Release  # → http://localhost:5005

API

Dev auth (replace with the V2 Identity Service + Kavenegar/SMS.ir later):

  • POST /api/auth/otp/request { phone }{ devCode: "1234" }
  • POST /api/auth/otp/verify { phone, code, name? }{ token, userId, name } (code 1234)
  • POST /api/auth/email { email, password, name? }{ token, userId, name }

SignalR hub: /hub/game (JWT required; pass ?access_token=<jwt>).

Client → server methods:

  • StartMatchmaking({ name, avatar, level, plan }) — pro skips the queue
  • CancelMatchmaking()
  • ChooseTrump(suit) · PlayCard(cardId) · SendReaction(reaction)

Server → client events:

  • matchmaking { phase, players, queuePosition }
  • matchFound { roomId, seat }
  • state GameStateDto (per-seat: only your own hand is included; others are counts)
  • reaction { seat, reaction }

The server runs server-side turn timers (20s → AI auto-plays), fills empty seats with bots, drives bot turns, and handles disconnect (the seat is marked offline and the timer auto-plays until they reconnect).

Wiring the Next.js client (next step)

Implement SignalrService in ../src/lib/online/signalr-service.ts against the existing OnlineService interface (@microsoft/signalr), then switch getService() in ../src/lib/online/service.ts from the mock to it. The hub's GameStateDto is shaped to map directly onto the client GameState.

Persistence (EF Core)

AppDbContext stores each profile as a JSON blob (ProfileRow) + a coin LedgerRow audit trail. Provider is config-driven:

// appsettings.json
"Database": { "Provider": "sqlite" },            // or "postgres"
"ConnectionStrings": { "Default": "Data Source=hokm.db" }
// Postgres (Supabase): Provider="postgres",
//   Default="Host=...;Database=...;Username=...;Password=...;SSL Mode=Require"

Schema is created at startup via EnsureCreated() (swap to EF migrations for prod).

Server-authoritative endpoints (JWT):

  • GET /api/profile · PUT /api/profile (displayName/avatar/title/cardFront/cardBack)
  • POST /api/profile/plan (upgrade to pro)
  • GET /api/coins/packs · POST /api/coins/buy { packId } (credits + ledger; real Zarinpal/IDPay TODO)
  • POST /api/match/result { MatchSummary } → computes rewards via Profiles/Gamification.cs (C# port of src/lib/online/gamification.ts), updates profile + ledger, returns RewardResult

Auth (/api/auth/...) upserts the profile on first sign-in.

TODO

  • Wire the Next client (SignalrService) to these endpoints for profile/coins/match (currently still mock-backed to avoid a half-migrated economy)
  • EF migrations; Postgres (Supabase) connection for prod
  • JWT issued by the V2 Identity Service; phone OTP via Kavenegar/SMS.ir
  • Private rooms + friend invites over the hub; server-side ranked entry deduction at match start