Files
HokmPlay/server/README.md
T
soroush.asadi aaf66b921f Phase G: scaffold .NET 10 + SignalR backend (engine port + hub + auth)
- server/ monorepo: Hokm.Engine (C# port of TS engine+AI, validated by sim),
  Hokm.Server (SignalR GameHub, in-memory matchmaking/rooms, server-side turn
  timers + bot fill + disconnect handling, per-seat state broadcast), Hokm.Sim
- JWT dev auth (OTP 1234 + email); CORS for the Next client; /hub/game
- NuGet restored from mirrors (Soroush Nexus + Liara); NuGetAudit off
- README + .NET .gitignore; static class Engine renamed Rules (namespace clash)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:42:15 +03:30

2.7 KiB

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.

TODO

  • EF Core + Postgres persistence (profiles, coins, rank, cosmetics, match history)
  • JWT issued by the V2 Identity Service; phone OTP via Kavenegar/SMS.ir
  • Private rooms + friend invites over the hub (engine/room already support 4 seats)
  • Server-side reward calculation (currently client/profile-side)