Files
soroush.asadi 4fb5a1776f
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 1m18s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m12s
CI/CD / Deploy - local stack (db + server + web) (push) Successful in 3m32s
fix(matchmaking): broadcast player list so queue avatars appear for all waiting players
BroadcastQueueLocked now sends the full waiting-player list (name/avatar/level)
alongside the count. The client maps it onto mm.players so every queued player's
avatar shows in the 4-slot grid for all waiting users, not just the slot-0 viewer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 19:46:28 +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