added documentation and decompiled client

This commit is contained in:
2026-03-12 00:27:00 +03:00
parent 669b8d7455
commit d7352b818d
6 changed files with 259 additions and 149 deletions
+64
View File
@@ -0,0 +1,64 @@
Proposed Direction
- Single core language: Go for all server runtime (gateway + lobby + game logic).
- Web/UI: TypeScript (Next.js or simple React/Vite) only.
- Goal: Keep binary protocol compatibility via a Gateway while the Core Monolith evolves.
———
Architecture (Monolith First)
[Game Client] ⇄ [Go Gateway] ⇄ [Go Core Monolith]
├─ Lobby module
├─ Matchmaking module
├─ Game loop module
└─ Data access layer
- Gateway: speaks binary protocol; maps opcodes to internal RPC/handlers.
- Core Monolith: simple Go service with clear module boundaries; no network protocol details here.
- Data: Postgres + Redis (optional) behind a DAL.
———
Phase 0: Baseline Observability (immediate)
- Add request/session IDs at the lobby entrypoint.
- Add JSON logs (opcode, account, session, latency).
- Add packet capture/replay harness (proxy or gateway shim).
This sets up parity testing for any rewrite.
———
Phase 1: Go Gateway (binary protocol fidelity)
- Implement binary parsing and encoding in Go.
- Map each opcode to a handler interface.
- For now, proxy handlers to legacy C servers (so clients keep working).
This becomes the compatibility anchor.
———
Phase 2: Go Core Monolith (first rewrite)
- Implement core modules in Go and progressively cut traffic over.
- Start with lobby/auth/deck APIs (lower risk).
- Add game loop later with deterministic state checks.
———
Phase 3: Data Migration
- Introduce Postgres schemas.
- Mirror writes (old → new), then flip reads.
- Eventually remove Couchbase.
———
Why this fits your preferences
- Only Go + TS.
- Monolith first with future separation if needed.
- Fully off C while maintaining client compatibility.
+189
View File
@@ -0,0 +1,189 @@
# Protocol Notes
## Opcode Decoder Map (Client)
Source: `hs-client/Assembly-CSharp/ConnectAPI.cs`.
- `116``PongPacketDecoder`
- `169``Deadend`
- `167``DeadendUtil`
- `123``DebugConsoleCommand`
- `124``DebugConsoleResponse`
- `14``AllOptions`
- `5``DebugMessage`
- `17``EntityChoices`
- `13``EntitiesChosen`
- `16``GameSetup`
- `19``PowerHistory`
- `15``UserUI`
- `9``TurnTimer`
- `10``NAckOption`
- `12``GameCanceled`
- `23``ServerResult`
- `24``SpectatorNotify`
- `289``Disconnected`
- `202``DeckList`
- `207``Collection`
- `215``GetDeckContentsResponse`
- `216``DBAction`
- `217``DeckCreated`
- `218``DeckDeleted`
- `219``DeckRenamed`
- `212``ProfileNotices`
- `224``BoosterList`
- `226``BoosterContent`
- `208``GamesInfo`
- `231``ProfileDeckLimit`
- `262``ArcaneDustBalance`
- `278``GoldBalance`
- `233``ProfileProgress`
- `270``PlayerRecords`
- `271``RewardProgress`
- `232``MedalInfo`
- `241``ClientOptions`
- `246``DraftBeginning`
- `247``DraftRetired`
- `248``DraftChoicesAndContents`
- `249``DraftChosen`
- `288``DraftRewardsAcked`
- `251``DraftError`
- `252``Achieves`
- `285``ValidateAchieveResponse`
- `282``CancelQuestResponse`
- `264``GuardianVars`
- `260``CardValues`
- `258``BoughtSoldCard`
- `269``MassDisenchantResponse`
- `265``BattlePayStatusResponse`
- `295``ThirdPartyPurchaseStatusResponse`
- `272``PurchaseMethod`
- `275``CancelPurchaseResponse`
- `256``PurchaseResponse`
- `238``BattlePayConfigResponse`
- `280``PurchaseWithGoldResponse`
- `283``HeroXP`
- `254``NoOpPacketDecoder`
- `286``PlayQueue`
- `330``CheckAccountLicensesResponse`
- `331``CheckGameLicensesResponse`
- `236``CardBacks`
- `292``SetCardBackResponse`
- `296``SetProgressResponse`
- `299``TriggerEventResponse`
- `300``NotSoMassiveLoginReply`
- `304``AssetsVersionResponse`
- `306``AdventureProgressResponse`
- `307``UpdateLoginComplete`
- `311``AccountLicenseAchieveResponse`
- `315``SubscribeResponse`
- `316``TavernBrawlInfo`
- `317``TavernBrawlPlayerRecordResponse`
- `318``FavoriteHeroesResponse`
- `320``SetFavoriteHeroResponse`
- `324``DebugCommandResponse`
- `325``AccountLicensesInfoResponse`
- `326``GenericResponse`
- `328``ClientRequestResponse`
- `322``GetAssetResponse`
## Opcode Encoder Map (Client → Server)
Game server outbound (QueueGamePacket):
- `22``SpectatorHandshake`
- `168``Handshake`
- `1``GetGameState`
- `115``Ping`
- `11``Concede`
- `3``ChooseEntities`
- `2``ChooseOption`
- `15``UserUI` (emote + mouse)
- `25``InviteToSpectate`
- `26``RemoveSpectators`
- `123``DebugConsoleCommand`
Debug console outbound (QueueDebugPacket):
- `124``DebugConsoleResponse`
Util server outbound (ClientRequestManager/UtilOutbound):
- `319``SetFavoriteHero`
- `279``PurchaseWithGold`
- `312``StartThirdPartyPurchase`
- `293``SubmitThirdPartyReceipt`
- `294``GetThirdPartyPurchaseStatus`
- `250``GetPurchaseMethod`
- `273``DoPurchase`
- `274``CancelPurchase`
- `237``GetBattlePayConfig`
- `255``GetBattlePayStatus`
- `268``MassDisenchantRequest`
- `235``DraftBegin`
- `242``DraftRetire`
- `287``DraftAckRewards`
- `244``DraftGetPicksAndContents`
- `245``DraftMakePick`
- `201``GetAccountInfo`
- `327``GenericRequestList`
- `205``UpdateLogin`
- `214``GetDeckContents`
- `209``CreateDeck`
- `210``DeleteDeck`
- `211``RenameDeck`
- `332``DeckSetTemplateSource`
- `222``DeckSetData`
- `213``AckNotice`
- `225``OpenBooster`
- `230``SetProgress`
- `298``TriggerLaunchDayEvent`
- `303``GetAssetsVersion`
- `308``AckWingProgress`
- `309``AcknowledgeBanner`
- `310``SetAdventureOptions`
- `223``AckCardSeen`
- `240``GetOptions`
- `239``SetOptions`
- `253``GetAchieves`
- `284``ValidateAchieve`
- `281``CancelQuest`
- `243``AckAchieveProgress`
- `297``CheckAccountLicenseAchieve`
- `305``GetAdventureProgress`
- `257``BuySellCard`
- `267``CheckAccountLicenses`
- `276``CheckGameLicenses`
- `291``SetCardBack`
- `321``GetAssetRequest`
- `329``Unsubscribe`
- `322``DebugCommandRequest`
## Learnings
- Client maintains deferred response maps for async requests in `Network.cs`.
- Opcode map is a key compatibility anchor for the Go gateway.
## Protobuf Definitions (Source)
The repo does not contain the protobuf class definitions for most message
types. They live in the client assemblies that were not decompiled here.
To generate Go structs, extract `.proto` from the client install:
- `PegasusGame.dll` (game server packets)
- `PegasusUtil.dll` (utility/account/collection packets)
- `SpectatorProto.dll` (spectator packets)
- `BobNetProto.dll` (misc/legacy)
- `PegasusShared.dll` (shared types: `CardDef`, `BnetId`, enums)
Suggested mapping (verify by decompiling those DLLs):
- `PegasusGame`: `GameSetup`, `PowerHistory`, `EntityChoices`, `EntitiesChosen`,
`UserUI`, `TurnTimer`, `NAckOption`, `GameCanceled`, `ServerResult`,
`Disconnected`, `Handshake`, `GetGameState`, `Ping`, `Concede`,
`ChooseEntities`, `ChooseOption`.
- `SpectatorProto`: `SpectatorHandshake`, `SpectatorNotify`, `InviteToSpectate`,
`RemoveSpectators`.
- `PegasusUtil`: everything in the decoder/encoder maps that relates to
collection, login, decks, purchases, achievements, and assets (most of the
`UtilOutbound` messages).
- `BobNetProto`: `Deadend`, `DeadendUtil` (verify).
-61
View File
@@ -1,61 +0,0 @@
## Development Environment Recommendations
This stack is most maintainable when the dev environment is repeatable and
configuration is centralized. The current scripts assume Ubuntu 16.04 and
hard-coded dependencies. The goal here is to make the environment predictable
without touching the protocol or rewriting core services.
Key recommendations:
- Standardize the dev workflow with containers (prefer `docker-compose`).
- Centralize ports, credentials, and paths into a single `.env` file.
- Provide a single entrypoint script to build/start/stop/log services.
- Keep database and service initialization scripted and idempotent.
## Focused Plan (Maintainability Only)
Phase 1: Audit and Baseline (13 days)
- Inventory the current scripts and dependencies in `hearthmod/`.
- Confirm service ports and startup order (gameserver → lobbyserver → web).
- Capture required Couchbase buckets and data seed steps.
Phase 2: Repeatable Local Environment (12 weeks)
- Add `docker-compose.yml` to orchestrate Couchbase, gameserver, lobbyserver,
and web services.
- Add `.env.example` for all required configuration values.
- Create a single `./dev` or `./scripts/dev.sh` entrypoint:
- `dev build` (build C services and web assets)
- `dev up` (start all services)
- `dev down` (stop services)
- `dev logs` (tail logs)
Phase 3: Configuration Cleanup (12 weeks)
- Replace hard-coded ports and credentials with env-configured values.
- Add a minimal config loader in `hm_web` and in C servers (config file or env).
- Update nginx config to use env-substituted paths.
Phase 4: Minimal Tests + Docs (1 week)
- Add a simple smoke test script that validates service ports and Couchbase
connectivity.
- Document the workflow in `README-dev.md` and update top-level README with a
pointer.
## Deliverables Checklist
- `docker-compose.yml` with defined services and volumes
- `.env.example` with required config variables
- A single dev entrypoint script
- Updated docs (this file) and a quick-start section
## Non-Goals (Intentionally Deferred)
- Updating Hearthstone protocol compatibility
- Rewriting the database or switching away from Couchbase
- Migrating C services to a different language
If you want, I can start by drafting the `docker-compose.yml` and a `dev` script
tailored to the current repo layout.
+5
View File
@@ -28,3 +28,8 @@ behavior while enabling modernization behind compatibility boundaries.
- Keep configuration centralized (env or config files).
- Prefer a single entrypoint script or container orchestration for local dev.
- Add small smoke tests to validate service health and database connectivity.
## Protocol Notes
See `PROTOCOL-NOTES.md` for the current opcode decoder map and client-derived
learnings used for compatibility work.
-88
View File
@@ -1,88 +0,0 @@
version: "3.8"
services:
couchbase:
image: ${COUCHBASE_IMAGE:-couchbase:community-6.6.0}
env_file: .env
ports:
- "8091-8096:8091-8096"
- "11210:11210"
volumes:
- couchbase_data:/opt/couchbase/var
gameserver:
build:
context: .
dockerfile: docker/Dockerfile.gameserver
env_file: .env
depends_on:
- couchbase
ports:
- "3724:3724"
volumes:
- ./hm_gameserver:/workspace/hm_gameserver
- ./hm_base:/workspace/hm_base
- ./hm_database:/workspace/hm_database
- ./hm_log:/workspace/hm_log
working_dir: /workspace
command: ["./hm_gameserver/hm_gameserver", "--log=/workspace/hm_log/hm_gameserver.log"]
lobbyserver:
build:
context: .
dockerfile: docker/Dockerfile.lobbyserver
env_file: .env
depends_on:
- couchbase
- gameserver
ports:
- "45678:45678"
volumes:
- ./hm_lobbyserver:/workspace/hm_lobbyserver
- ./hm_base:/workspace/hm_base
- ./hm_database:/workspace/hm_database
- ./hm_log:/workspace/hm_log
working_dir: /workspace
command:
- "./hm_lobbyserver/hm_lobbyserver"
- "--gameserver=gameserver"
- "--log=/workspace/hm_log/hm_lobbyserver.log"
sunwell:
build:
context: ./hm_sunwell
dockerfile: docker/Dockerfile.sunwell
env_file: .env
ports:
- "3000:3000"
web:
build:
context: ./hm_web
dockerfile: docker/Dockerfile.web
env_file: .env
depends_on:
- couchbase
- sunwell
ports:
- "9002:9002"
volumes:
- ./hm_web:/workspace/hm_web
- ./hm_sunwell:/workspace/hm_sunwell
working_dir: /workspace/hm_web
command: ["python", "app.py"]
nginx:
build:
context: ./hm_nginx
dockerfile: docker/Dockerfile.nginx
env_file: .env
depends_on:
- web
ports:
- "8080:80"
volumes:
- ./hm_web/static:/usr/local/web/static:ro
volumes:
couchbase_data:
Submodule
+1
Submodule hs-client added at 67b3ddf12c