mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-08-28 23:01:09 +00:00
e5c830e945
* digimon digi battle added to supported games * sonarqube update * readme update --------- Co-authored-by: sdine <sdine@sdine.com>
7.3 KiB
7.3 KiB
app/AGENTS.md
The ccm executable — composition root only. The single place where concrete adapter types are mentioned. Read the root AGENTS.md first.
File pointers
main.cpp— the entire app. DefinesCcmApp : public wxApp, builds the dependency graph inOnInit(), then hands anAppContexttoMainFrame.CMakeLists.txt— declares theccmtarget. SetsWIN32_EXECUTABLE TRUEon Windows so no console window appears. Linksccm_core,ccm_ui_wx,ccm_warnings.POST_BUILD: creates$<TARGET_FILE_DIR:ccm>/assets/and copiesui_wx/assets/ygo_card_back.pngandui_wx/assets/digibattle99_card_back.pngthere so Yu-Gi-Oh! / Digi-Battle preview fallbacks work offline (seeBaseSelectedCardPanel/docs/assets-and-info-apis.md).
Conventions
- Composition root is the only place that names concrete adapters:
StdFileSystem,CprHttpClient,JsonCollectionRepository<MagicCard>,JsonCollectionRepository<PokemonCard>,JsonCollectionRepository<YuGiOhCard>,JsonCollectionRepository<DigiBattle99Card>,JsonSetRepository,LocalImageStore,LocalPreviewByteCache,MagicGameModule,PokemonGameModule,YuGiOhGameModule,DigiBattle99GameModule,MagicGameView,PokemonGameView,YuGiOhGameView,DigiBattle99GameView, etc. If a concrete adapter type appears anywhere else in the codebase, move the wiring here. - Member declaration order in
CcmAppmatters — destruction is reverse, so a member that depends on another (e.g.magicCollSvc_depends onmagicRepo_andimgStore_;previewSvc_depends onhttp_and is consumed byctx_;magicView_depends on the typedmagicCollSvc_and the shared services) must be declared after its deps. Do not reorder casually. - Use
std::unique_ptrfor everything owned byCcmApp. TheAppContextthen holds plain references into those owned objects, plus a vector ofIGameView*raw pointers (theunique_ptr<>s for the views are the actual owners; the vector just describes the active set). - Game-to-directory mapping lives in
dirNameForGame(Game)(anonymous namespace). When adding a new game, extend this function — it is wired into all three repositories (JsonCollectionRepository,JsonSetRepository,LocalImageStore). config.jsonlocation is the executable's parent directory, resolved viawxStandardPaths::Get().GetExecutablePath(). Do not change this — existing installations rely on that location.- Image format handlers must be registered via
wxImage::AddHandler(new wxPNGHandler)andnew wxJPEGHandlerbefore any image is loaded. They are added inOnInit()first thing — keep it that way. - Card preview source ownership lives inside the
IGameModule. The composition root never constructs an<Name>CardPreviewSourcedirectly; it callspreviewSvc_->registerModule(*<name>Mod_)and the service pulls the module's preview source viaIGameModule::cardPreviewSource()(returningnullptris silently skipped). - One
CprHttpClientper app, shared by every consumer. The singlehttp_instance is handed toSetService,CardPreviewService, and every per-game module. Do not construct a secondCprHttpClient(or passcpr::Get(...)directly) from anywhere — the adapter holds a long-livedcpr::Sessionwhose connection cache + TLS keep-alive is what makes repeat lookups fast (game-agnostic; seecore/AGENTS.mdconvention 11). The shared instance also givesCardPreviewService's in-memory LRU a single source of truth to cache against. - One
LocalPreviewByteCacheper app, rooted at<exeDir>/.cache/preview-cache/— i.e. next to the executable, in the same scope asconfig.json. Do not root the cache atconfig_->current().dataStorage: the user's data-storage path is user-configurable at runtime and is meant for the user's collection (cards, scans, set lists). Previews are downloaded-from-network artifacts that (a) must not move when the user relocates their collection, (b) must not be uploaded/synced together with the user's data dir, and (c) must not survive a fresh install elsewhere on disk. Pinning the cache toexeDiris what gives those properties without writing extra plumbing for each data-storage flow. The umbrella.cache/directory is reserved for any future computed-from-network caches (set-list snapshots, etc.); the leading dot keeps it out of the way for users poking around the install folder. Cache updates flow entirely through cache keys:CardPreviewServiceinvalidates entries automatically when the cache key changes (record edits) and rewrites them when a same-key resolution flips between positive and negative — there is noclearCache(...)API. To wipe the cache manually, delete<exeDir>/.cache/; reinstalling / moving the executable also resets the cache by design. Construct the cache afterConfigService(so the dependency graph is the same as before; the cache itself only needs*fs_and the resolvedexeDir) and beforeCardPreviewService(so the service can hold a stable raw pointer); declare the member afterconfig_/fs_and beforepreviewSvc_to keep destruction order correct. Seecore/AGENTS.mdconvention 10 anddocs/caching.md("Updating cached entries") for the full cache shape, policy, and update mechanic.
Required follow-ups
- The
POST_BUILDcopy ofygo_card_back.png/digibattle99_card_back.pngmust stay in sync withui_wx/assets/; if you relocate install layout or add more bundled assets, mirror the pattern (make_directory+copy_if_different) and document underdocs/assets-and-info-apis.md/ui_wx/AGENTS.md. - After adding a new game module you must: (1) add a
unique_ptr<<Name>GameModule>member in declaration-order-correct position, (2) construct it inOnInit(), (3) callsetSvc_->registerModule(<name>Mod_.get()), (4) callpreviewSvc_->registerModule(*<name>Mod_)(no-op when the module has no preview source), (5) extenddirNameForGame, (6) add a typedJsonCollectionRepository<<Name>Card>+CollectionService<<Name>Card>if the game has a custom card type, (7) construct a<Name>GameViewand append its raw pointer to theAppContext::gameViewsvector, (8) make sure the view'sunique_ptr<>member sits after all its deps (typed services +IGameModule). - After adding a new core service you must add a
unique_ptr<...>member, construct it inOnInit()after its deps, and add a reference field toAppContext. - After adding a new dependency edge you must verify destruction order is still correct: deps before dependents in the member list.
- After changing the IGameView contract or the AppContext shape, update
docs/adding-a-new-game.mdso the canonical procedure stays in sync.
Anti-patterns
- Don't add business logic here. If something is more than
std::make_uniqueand aregister/Bindcall, it belongs incore/. - Don't construct services on the stack inside
OnInit()— they must outlive theMainFrame, so they live asCcmAppmembers.
Commands
- Build the binary:
cmake --build build --target ccm - Run on Windows / MinGW-w64:
.\build\bin\ccm3.exe. The cpr/curl/zlib DLLs are placed next to the exe automatically; the MSYS2 UCRT64 runtime (libgcc_s_seh-1.dll,libstdc++-6.dll) needs to be onPATH(e.g.P:\msys2\msys64\ucrt64\bin). On verified runs the exe loads under window title "Card Collection Manager 3".