diff --git a/Readme.md b/Readme.md index 9415c78..8198134 100644 --- a/Readme.md +++ b/Readme.md @@ -1170,8 +1170,10 @@ Paths are **relative** to `persistentDataPath`. Never store absolute paths — t ## 29. Boot & Error Handling +> **Status: not implemented.** No `AppBoot` class exists. Today, [RootLifetimeScope.cs](Assets/Darkmatter/Code/App/LifetimeScopes/RootLifetimeScope.cs) only iterates installer MonoBehaviours and registers them — nothing runs after that. The block below is the *target* sequence when `AppBoot` is added as an `IAsyncStartable` entry point under `App/Boot/`. + ``` -AppBoot.StartAsync() +AppBoot.StartAsync() (planned — Features/Boot/AppBoot.cs, registered via builder.RegisterEntryPoint()) ├─ try Addressables.InitializeAsync() │ fail → show "Tap to retry" splash ├─ try preload palette + UI sounds (Addressables labels) @@ -1227,6 +1229,8 @@ If a class's natural home doesn't match its asmdef, the architecture is bent — ## 32. Class Reference (Detailed) +> **Status: target spec, mostly unimplemented.** Of everything below, only the following Service classes exist on disk today: `AddressableAssetProviderService`, `AudioService` / `SfxPlayer`, `CameraService`, `SceneService`, `InputReaderSO`, `FirebaseAnalyticsSystem`. Everything else (Paper, Drawing, Coloring, History, Capture, Gallery, Progression, ColorBookFlow, ArtBook, AppBoot) is the target shape for when those classes are written. Treat this section as a contract for new code, not documentation of current state. + Canonical breakdown of every concrete class and interface. For each: **purpose**, **public surface** (signatures), **injected dependencies**, and **collaborators** (signals or interfaces it talks to). > Convention used below @@ -1234,6 +1238,7 @@ Canonical breakdown of every concrete class and interface. For each: **purpose** > - `// pub:` = events / signals fired > - `// sub:` = events / signals consumed > - All async returns are `UniTask` unless noted. +> - Folder labels follow the actual nesting pattern: `Core/Contracts/Features//`, `Core/Contracts/Services//`, `Core/Data/Dynamic/Features//`, `Features///`, `Services///`. --- @@ -1241,7 +1246,7 @@ Canonical breakdown of every concrete class and interface. For each: **purpose** Pure interfaces and DTOs. Zero logic. -#### `IDrawingTemplate` *(Core/Drawing)* +#### `IDrawingTemplate` *(Core/Contracts/Features/Drawing — planned)* Immutable view of a single drawing's authored data. ```csharp public interface IDrawingTemplate { @@ -1255,7 +1260,7 @@ public interface IDrawingTemplate { ``` Implemented by `DrawingTemplateSO` (ScriptableObject) loaded via Addressables. -#### `IDrawingTemplateCatalog` *(Core/Drawing)* +#### `IDrawingTemplateCatalog` *(Core/Contracts/Features/Drawing — planned)* Authority on which drawings exist, completion state, and "next" selection. ```csharp public interface IDrawingTemplateCatalog { @@ -1268,7 +1273,7 @@ public interface IDrawingTemplateCatalog { } ``` -#### `IColorPalette` *(Core/Coloring)* +#### `IColorPalette` *(Core/Contracts/Features/Coloring — planned)* Set of colors offered to the child. Authored as `ColorPaletteSO`. ```csharp public interface IColorPalette { @@ -1277,10 +1282,10 @@ public interface IColorPalette { } ``` -#### `ICommand` & `IUndoStack` *(Core/History)* +#### `ICommand` & `IUndoStack` *(Core/Contracts/Features/History — planned)* Already shown in section 8. Each undoable user action is one `ICommand`; the stack is bounded. -#### `IGalleryService` *(Core/Gallery)* +#### `IGalleryService` *(Core/Contracts/Services/Gallery — planned)* Persistent store of saved artwork PNGs. ```csharp public interface IGalleryService { @@ -1292,7 +1297,7 @@ public interface IGalleryService { } ``` -#### `ICaptureService` *(Core/Capture)* +#### `ICaptureService` *(Core/Contracts/Services/Capture — planned)* Snapshots the paper RT to a PNG blob. No arguments — dimensions and content come from `IPaperRig.Surface`. ```csharp public interface ICaptureService { @@ -1300,7 +1305,7 @@ public interface ICaptureService { } ``` -#### `IPaperRig` *(Core/Paper)* +#### `IPaperRig` *(Core/Contracts/Features/Paper — planned)* Shared art rig. The single source of truth for everything that lives in the drawing world. ```csharp public interface IPaperRig { @@ -1312,7 +1317,7 @@ public interface IPaperRig { } ``` -#### `IArtInputBridge` *(Core/Paper)* +#### `IArtInputBridge` *(Core/Contracts/Features/Paper — planned)* Converts screen-space pointer coords to art-world coords inside the RT. ```csharp public interface IArtInputBridge { @@ -1321,7 +1326,7 @@ public interface IArtInputBridge { ``` Returns `false` when the pointer is outside the displayed RawImage rect (toddler tapped the HUD or backdrop). Every art-world raycast goes through this. -#### `IProgressionService` *(Core/Progression)* +#### `IProgressionService` *(Core/Contracts/Features/Progression — planned)* Tracks which templates the child has completed and what they last opened. ```csharp public interface IProgressionService { @@ -1334,7 +1339,7 @@ public interface IProgressionService { } ``` -#### `IAssetProviderService` *(Core/Assets)* +#### `IAssetProviderService` *(Core/Contracts/Services/Assets — ✅ exists)* Addressables wrapper. Hides handle bookkeeping from features. ```csharp public interface IAssetProviderService { @@ -1346,7 +1351,7 @@ public interface IAssetProviderService { } ``` -#### `IEventBus` *(Libs/EventBus, also referenced from Core)* +#### `IEventBus` *(Libs/Observer — ✅ exists; note the folder is `Observer`, not `EventBus`)* ```csharp public interface IEventBus { void Publish(T signal) where T : struct; @@ -1359,15 +1364,15 @@ Signals are structs to avoid GC. Disposable subscription so presenters can unsub ### 32.2 Services Layer -Concrete infrastructure. One implementation each. All singletons in `RootLifetimeScope`. +Concrete infrastructure. One implementation each. All singletons in `RootLifetimeScope`, registered via per-service `MonoBehaviour, IServiceModule` installers. -#### `AddressableAssetProviderService` *(Services/Assets)* +#### `AddressableAssetProviderService` *(Services/Assets — ✅ exists)* Implements `IAssetProviderService`. - **Responsibility:** Wrap `Addressables.LoadAssetAsync` and ref-count handles by address. - **State:** `Dictionary` keyed by address. - **Notes:** `Release(address)` decrements; `ReleaseAll()` for scene teardown. Initialization must complete before any other service may load. -#### `FileGalleryService` *(Services/Gallery)* +#### `FileGalleryService` *(Services/Gallery — planned)* Implements `IGalleryService`. ```csharp // fields: @@ -1380,13 +1385,13 @@ Implements `IGalleryService`. - **List flow:** enumerate `*.json` in `Gallery/`, deserialize, sort by `CreatedUtc desc`. - **Delete flow:** delete png + thumb + json; missing files ignored (idempotent). -#### `RenderTextureCaptureService` *(Services/Capture)* +#### `RenderTextureCaptureService` *(Services/Capture — planned)* Implements `ICaptureService`. - **Steps:** allocate `RenderTexture(width, height, 0, ARGB32)` → bind to `artCamera.targetTexture` → `artCamera.Render()` → `ReadPixels` into `Texture2D` → composite `paperBackground` underneath (single shader blit) → `EncodeToPNG` → release RT + textures. - **Threading:** PNG encode happens on a `UniTask.RunOnThreadPool` to avoid hitching the main thread on tablets. - **Sizing:** default 2048², overridable. Capped at device max texture size. -#### `JsonPersistenceService` *(Services/Persistence)* +#### `JsonPersistenceService` *(Services/Persistence — planned; today `Libs/PlayerPrefs` covers small-key state)* Implements `IPersistenceService` (small JSON blob; not the gallery). ```csharp public interface IPersistenceService { @@ -1398,7 +1403,7 @@ public interface IPersistenceService { - **Format:** single JSON object keyed by `key` so multiple services can share one file. - **Atomicity:** write to `save.json.tmp` → rename. -#### `SceneService` *(Services/Scenes)* +#### `SceneService` *(Services/Scenes — ✅ exists)* Implements `ISceneService`. Wraps `SceneManager.LoadSceneAsync` with `UniTask` plus a fade-curtain. ```csharp public interface ISceneService { @@ -1407,7 +1412,7 @@ public interface ISceneService { } ``` -#### `AudioService` *(Services/Audio)* +#### `AudioService` *(Services/Audio — ✅ exists; see also `SfxPlayer`)* Implements `IAudioService`. Plays SFX clips loaded by address, mixes via Unity AudioMixer groups. ```csharp public interface IAudioService { @@ -1418,7 +1423,7 @@ public interface IAudioService { ``` Holds an internal `Dictionary` populated at preload. -#### `InputReaderSO` *(Services/Inputs)* +#### `InputReaderSO` *(Services/Inputs/Readers — ✅ exists)* ScriptableObject wrapping the new Input System; exposes events. ```csharp public interface IInputReader { @@ -1435,19 +1440,19 @@ public interface IInputReader { Generic, project-agnostic utilities. -#### `BoundedUndoStack` *(Libs/CommandStack)* +#### `BoundedUndoStack` *(Libs/CommandStack — planned)* Implements `IUndoStack`. Source already in section 24. - **Capacity:** default 20. - **Invariant:** `_redo` cleared on any new `Push`. - **Edge cases:** `Undo`/`Redo` on empty stack is a no-op (never throws). -#### `EventBus` *(Libs/EventBus)* +#### `EventBus` *(Libs/Observer — ✅ exists)* Implements `IEventBus` with a `Dictionary` of `Action` per signal type. - **Subscribe** returns an `IDisposable` that removes the handler on `Dispose`. - **Publish** snapshots the invocation list before iterating (so handlers may safely unsubscribe during dispatch). -#### `Fsm` *(Libs/FSM)* -Generic state machine used by `ColorBookFlowController`. +#### `StateMachine` / `IState` / `State` *(Libs/FSM — ✅ exists)* +Generic state machine. Current shape on disk uses `IState` / `State` / `StateMachine` (see [Libs/FSM/](Assets/Darkmatter/Code/Libs/FSM/)). `ColorBookFlowController` (planned) will use this. The generic sketch below is the target shape if you decide to make it strongly-typed via an enum — verify against actual API before consuming. ```csharp public sealed class Fsm where TState : struct, Enum { public TState Current { get; } @@ -1461,7 +1466,7 @@ public interface IFsmState { void Enter(); void Exit(); } --- -### 32.4 Feature — `DrawingCatalog` +### 32.4 Feature — `DrawingCatalog` *(planned)* #### `DrawingCatalogController` *(Systems)* Headless logic. Owns the list of template IDs visible in the grid. @@ -1498,7 +1503,7 @@ public interface IDrawingCatalogView { --- -### 32.5 Feature — `ShapeBuilder` +### 32.5 Feature — `ShapeBuilder` *(planned)* #### `ShapeBuilderController` *(Systems)* Spawns shape pieces for the selected template, tracks snap progress, fires `ShapeAssembledSignal` when complete. @@ -1537,7 +1542,7 @@ public sealed class ShapePieceFactory { --- -### 32.5b Feature — `Paper` +### 32.5b Feature — `Paper` *(planned)* The shared art rig — RT, offscreen camera, screen↔world bridge. Every other feature in the ColorBook scene resolves `IPaperRig` and `IArtInputBridge` from DI and never touches `Screen.*` or `Camera.*` directly. @@ -1608,7 +1613,7 @@ public void Register(IContainerBuilder builder) { --- -### 32.6 Feature — `Coloring` +### 32.6 Feature — `Coloring` *(planned)* #### `ColoringStateRepository` *(Repository)* In-memory model. Owns "currently selected color" and the palette in use. @@ -1670,7 +1675,7 @@ Mirror of `ShapePieceFactory` for regions. Pool-friendly. --- -### 32.7 Feature — `History` +### 32.7 Feature — `History` *(planned)* #### `HistoryController` *(Systems)* — `IStartable, IDisposable` Owns the per-session `IUndoStack` (registered scoped, so a new ColorBook scene = new stack). @@ -1702,7 +1707,7 @@ Wires controller `StateChanged` ↔ view enable/disable; view click events → c --- -### 32.8 Feature — `Capture` +### 32.8 Feature — `Capture` *(planned)* #### `CaptureController` *(Systems)* The orchestrator behind the "Capture" button. Stateless other than guarding against concurrent captures. @@ -1723,7 +1728,7 @@ Wires button click → `CaptureController.CaptureCurrentAsync`. Disables button --- -### 32.9 Feature — `Progression` +### 32.9 Feature — `Progression` *(planned)* #### `ProgressionService` *(Systems)* — implements `IProgressionService` The only place that knows what "completed" means. @@ -1736,7 +1741,7 @@ Pure in-memory holder used by the service. Separated so tests can inspect state --- -### 32.10 Feature — `ColorBookFlow` +### 32.10 Feature — `ColorBookFlow` *(planned)* #### `ColorBookFlowController` *(Systems)* — `IStartable, IDisposable` **The only orchestrator inside the ColorBook scene.** Drives the panel FSM: `Catalog → Building → Coloring → Done`. @@ -1764,7 +1769,7 @@ Pure in-memory holder used by the service. Separated so tests can inspect state --- -### 32.11 Feature — `ArtBook` +### 32.11 Feature — `ArtBook` *(planned)* #### `GalleryPresenter` *(UI)* — `IAsyncStartable, IDisposable` Lists artworks, opens fullscreen view, deletes, shares. @@ -1799,7 +1804,7 @@ public interface IExternalShareService { ### 32.12 App Layer -#### `AppBoot` *(App/Boot)* — `IAsyncStartable` +#### `AppBoot` *(App/Boot — planned; folder doesn't exist yet)* — `IAsyncStartable` Single entry point. Steps in section 29. ```csharp // fields: IAssetProviderService _assets, IPersistenceService _persist, IProgressionService _progress, @@ -1810,25 +1815,29 @@ public sealed class AppBoot : IAsyncStartable { ``` #### LifetimeScopes -- `RootLifetimeScope` — section 21. Registers all services + `IEventBus`. Persists for app lifetime. -- `MainMenuLifetimeScope` — registers `MainMenuPresenter` and view. -- `ColorBookLifetimeScope` — section 21. Registers feature installers + `ColorBookFlowController` as entry point. -- `ArtBookLifetimeScope` — registers `GalleryPresenter` + view + `IExternalShareService`. +- `RootLifetimeScope` — ✅ exists ([source](Assets/Darkmatter/Code/App/LifetimeScopes/RootLifetimeScope.cs)). Iterates a serialized `MonoBehaviour[] serviceModules` and calls `Register` on each `IServiceModule`. Persists for app lifetime. +- `MainMenuLifetimeScope` — planned. Same pattern as Root (serialized installer list, no hardcoded registrations). +- `ColorBookLifetimeScope` — planned. Same pattern; installer list includes `PaperRigModule`, feature installers, and the flow controller installer. +- `ArtBookLifetimeScope` — planned. -All scope classes are thin: serialized fields for scene refs, `Configure(IContainerBuilder)` only. +All scope classes are thin: a serialized installer-MonoBehaviour list (+ optional scene refs as separate fields) and a `Configure(IContainerBuilder)` that iterates and calls `Register`. --- ### 32.13 Cross-cutting types -#### `ColorBookSceneRefs : MonoBehaviour` *(App)* -Aggregates all scene-bound Unity references that features need: `Camera artCamera`, `Transform catalogRoot`, `Transform builderRoot`, `Transform coloringRoot`, `RectTransform hudRoot`, `ColorPaletteView paletteView`, `HistoryButtonsView historyView`. Registered as a singleton in `ColorBookLifetimeScope` so features don't `Find` things. +#### `ColorBookSceneRefs : MonoBehaviour` *(App — planned)* +Aggregates scene-bound Unity references that features need: `Camera artCamera`, `Transform catalogRoot`, `Transform builderRoot`, `Transform coloringRoot`, `RectTransform hudRoot`, `ColorPaletteView paletteView`, `HistoryButtonsView historyView`. Registered in `ColorBookLifetimeScope` via `builder.RegisterInstance(_sceneRefs)` so features don't `Find` things. -#### `IInstaller` *(App)* +> Most of these refs are subsumed by `IPaperRig` now (which owns `ArtCamera` and `PaperRoot`). `ColorBookSceneRefs` reduces to the HUD-side refs (palette view, history buttons, panel roots). + +#### `IServiceModule` *(Libs/Installers — ✅ exists)* ```csharp -public interface IInstaller { void Install(IContainerBuilder builder); } +public interface IServiceModule { + void Register(IContainerBuilder builder); +} ``` -Implemented as `ScriptableObject` per feature so scopes can drag them in the inspector (section 22). +Implemented as `MonoBehaviour` per feature/service so scopes can drag them in the inspector ([CameraServiceModule.cs](Assets/Darkmatter/Code/Services/Camera/Installers/CameraServiceModule.cs) shows the pattern). The method is `Register`, not `Install` — there is no `IInstaller` in this project. --- @@ -1866,3 +1875,5 @@ Implemented as `ScriptableObject` per feature so scopes can drag them in the ins | `ProgressionService` | Service | Completion tracking | persistence | If you add a class not in this table, add it here in the same PR. This table is the cheap mental-model index — keep it honest. + +> Today only these rows are real on disk: `RootLifetimeScope` (App), `AddressableAssetProviderService` (Service), `AudioService` (Service), `CameraService` (Service), `SceneService` (Service), `InputReaderSO` (Service), plus the Firebase analytics class, plus the `Libs.*` entries (`EventBus`, `StateMachine`, `IServiceModule`, PlayerPrefs lib, UI toggles). Everything else is the target.