|
| 1 | +# GameLovers.Services - AI Agent Guide |
| 2 | + |
| 3 | +## 1. Package Overview |
| 4 | +- **Package**: `com.gamelovers.services` |
| 5 | +- **Unity**: 6000.0+ |
| 6 | +- **Dependencies** (see `package.json`) |
| 7 | + - `com.gamelovers.dataextensions` (**0.6.2**) (contains `floatP`, used by `RngService`) |
| 8 | + |
| 9 | +This package provides a set of small, modular “foundation services” for Unity projects (service locator/DI-lite, messaging, ticking, coroutines, pooling, persistence, RNG, time, and build version helpers). |
| 10 | + |
| 11 | +For user-facing docs, treat `README.md` as the primary entry point. This file is for contributors/agents working on the package itself. |
| 12 | + |
| 13 | +## 2. Runtime Architecture (high level) |
| 14 | +- **Service locator / bindings**: `Runtime/Installer.cs`, `Runtime/MainInstaller.cs` |
| 15 | + - `Installer` stores a `Dictionary<Type, object>` of interface type → instance. |
| 16 | + - `MainInstaller` is a **static** wrapper over a single `Installer` instance (global scope). |
| 17 | + - Binding is **instance-based** (`Bind<T>(T instance)`), not “type-to-type” or lifetime-managed DI. |
| 18 | + - Only **interfaces** can be bound (binding a non-interface throws). |
| 19 | +- **Messaging**: `Runtime/MessageBrokerService.cs` |
| 20 | + - Message contract: `IMessage` |
| 21 | + - Pub/sub via `IMessageBrokerService` (`Publish`, `PublishSafe`, `Subscribe`, `Unsubscribe`, `UnsubscribeAll`) |
| 22 | + - Stores subscribers keyed by `action.Target` (so **static method subscriptions are not supported**). |
| 23 | +- **Tick / update fan-out**: `Runtime/TickService.cs` |
| 24 | + - Creates a `DontDestroyOnLoad` GameObject with `TickServiceMonoBehaviour` to drive Update/LateUpdate/FixedUpdate callbacks. |
| 25 | + - Supports “buffered” ticking (`deltaTime`) and optional overflow carry (`timeOverflowToNextTick`) to reduce drift. |
| 26 | + - Uses scaled (`Time.time`) or unscaled (`Time.realtimeSinceStartup`) time depending on `realTime`. |
| 27 | +- **Coroutine host**: `Runtime/CoroutineService.cs` |
| 28 | + - Creates a `DontDestroyOnLoad` GameObject with `CoroutineServiceMonoBehaviour` to run coroutines from pure C# code. |
| 29 | + - `IAsyncCoroutine` / `IAsyncCoroutine<T>` wraps a Unity `Coroutine` and offers completion callbacks + state flags. |
| 30 | +- **Pooling**: |
| 31 | + - Pool registry: `Runtime/PoolService.cs` (`PoolService : IPoolService`) |
| 32 | + - Pool implementations: `Runtime/ObjectPool.cs` |
| 33 | + - Generic `ObjectPool<T>` |
| 34 | + - Unity-specific: `GameObjectPool`, `GameObjectPool<TBehaviour>` |
| 35 | + - Lifecycle hooks: `IPoolEntitySpawn`, `IPoolEntitySpawn<TData>`, `IPoolEntityDespawn`, `IPoolEntityObject<T>` |
| 36 | +- **Persistence**: `Runtime/DataService.cs` |
| 37 | + - In-memory store keyed by `Type` |
| 38 | + - Disk persistence via `PlayerPrefs` + `Newtonsoft.Json` serialization |
| 39 | +- **Time + manipulation**: `Runtime/TimeService.cs` |
| 40 | + - `ITimeService` + `ITimeManipulator` for querying time (Unity / Unix / DateTime UTC) and applying offsets. |
| 41 | +- **Deterministic RNG**: `Runtime/RngService.cs` |
| 42 | + - Deterministic RNG state stored in `RngData` and exposed via `IRngData`. |
| 43 | + - Float API uses `floatP` (from `com.gamelovers.dataextensions`) for deterministic float math. |
| 44 | +- **Build/version info**: `Runtime/VersionServices.cs` |
| 45 | + - Runtime access to version strings and git/build metadata loaded from a Resources TextAsset. |
| 46 | + |
| 47 | +## 3. Key Directories / Files |
| 48 | +- **Runtime**: `Runtime/` |
| 49 | + - Entry points: `MainInstaller.cs`, `Installer.cs` |
| 50 | + - Services: `MessageBrokerService.cs`, `TickService.cs`, `CoroutineService.cs`, `PoolService.cs`, `DataService.cs`, `TimeService.cs`, `RngService.cs`, `VersionServices.cs` |
| 51 | + - Pooling: `ObjectPool.cs` |
| 52 | +- **Editor**: `Editor/` |
| 53 | + - Version data generation: `VersionEditorUtils.cs`, `GitEditorProcess.cs` |
| 54 | + - Must remain editor-only (relies on `UnityEditor` + starting git processes) |
| 55 | +- **Tests**: `Tests/` |
| 56 | + - EditMode/PlayMode tests validating service behavior |
| 57 | + |
| 58 | +## 4. Important Behaviors / Gotchas |
| 59 | +- **`MainInstaller` API vs README snippets** |
| 60 | + - `MainInstaller` is a static class exposing `Bind/Resolve/TryResolve/Clean`. |
| 61 | + - If you see docs/examples referring to `MainInstaller.Instance` or fluent bindings, verify against runtime code—those snippets may be stale. |
| 62 | +- **Message broker mutation safety** |
| 63 | + - `Publish<T>` iterates subscribers directly; subscribing/unsubscribing during publish is blocked and throws. |
| 64 | + - Use `PublishSafe<T>` if you have chain subscriptions/unsubscriptions during message handling (it copies delegates first, at extra allocation cost). |
| 65 | + - `Subscribe` uses `action.Target` as the subscriber key, so **static methods cannot subscribe**. |
| 66 | +- **Tick/coroutine services allocate a global GameObject** |
| 67 | + - `TickService` and `CoroutineService` each create a `DontDestroyOnLoad` GameObject. Call `Dispose()` when you want to tear them down (tests, game reset, domain reload edge cases). |
| 68 | + - These services do **not** enforce a singleton at runtime; constructing multiple instances will create multiple host GameObjects. |
| 69 | +- **`IAsyncCoroutine.StopCoroutine(triggerOnComplete)`** |
| 70 | + - The current implementation triggers completion callbacks even when `triggerOnComplete` is `false` (parameter is not respected). Keep this in mind if you rely on cancellation semantics. |
| 71 | +- **DataService persistence details** |
| 72 | + - Keys are `typeof(T).Name` in `PlayerPrefs` (name collisions are possible across assemblies/types with same name). |
| 73 | + - `LoadData<T>` requires `T` to have a parameterless constructor (via `Activator.CreateInstance<T>()`) if no data exists. |
| 74 | +- **Pool lifecycle** |
| 75 | + - `PoolService` keeps **one pool per type**; it does not guard against duplicate `AddPool<T>()` calls (duplicate adds throw from `Dictionary.Add`). |
| 76 | + - `GameObjectPool.Dispose(bool)` destroys the `SampleEntity` GameObject; `GameObjectPool.Dispose()` destroys pooled instances but does not necessarily destroy the sample reference—be explicit about disposal expectations when changing pool behavior. |
| 77 | +- `GameObjectPool` and `GameObjectPool<T>` override `CallOnSpawned`/`CallOnDespawned` (virtual methods) to use `GetComponent<IPoolEntitySpawn>()` / `GetComponent<IPoolEntityDespawn>()` for lifecycle hooks on **components**. This differs from `ObjectPool<T>` which casts the entity directly. |
| 78 | +- **Version data pipeline** |
| 79 | + - Runtime expects a Resources TextAsset named `version-data` (`VersionServices.VersionDataFilename`). |
| 80 | + - `VersionEditorUtils` writes `Assets/Configs/Resources/version-data.txt` on editor load and can be invoked before builds. It uses git CLI; failures should be handled gracefully. |
| 81 | + - Accessors like `VersionServices.VersionInternal` will throw if version data hasn’t been loaded yet—call `VersionServices.LoadVersionDataAsync()` early (and decide how you want to handle load failures). |
| 82 | + |
| 83 | +## 5. Coding Standards (Unity 6 / C# 9.0) |
| 84 | +- **C#**: C# 9.0 syntax; explicit namespaces; no global usings. |
| 85 | +- **Assemblies** |
| 86 | + - Runtime must not reference `UnityEditor`. |
| 87 | + - Editor tooling must live under `Editor/` (or be guarded with `#if UNITY_EDITOR` if absolutely necessary). |
| 88 | +- **Performance** |
| 89 | + - Be mindful of allocations in hot paths (e.g., `PublishSafe` allocates; tick lists mutate; avoid per-frame allocations). |
| 90 | + |
| 91 | +## 6. External Package Sources (for API lookups) |
| 92 | +Prefer local UPM cache / local packages when needed: |
| 93 | +- DataExtensions: `Packages/com.gamelovers.dataextensions/` (e.g., `floatP`) |
| 94 | +- Unity Newtonsoft JSON (Unity package): check `Library/PackageCache/` if you need source details |
| 95 | + |
| 96 | +## 7. Dev Workflows (common changes) |
| 97 | +- **Add a new service** |
| 98 | + - Add runtime interface + implementation under `Runtime/` (keep UnityEngine usage minimal if possible). |
| 99 | + - Add/adjust tests under `Tests/`. |
| 100 | + - If the service needs Unity callbacks, follow the `TickService`/`CoroutineService` pattern (single `DontDestroyOnLoad` host object + `Dispose()`). |
| 101 | +- **Bind/resolve services** |
| 102 | + - Bind instances via `MainInstaller.Bind<IMyService>(myServiceInstance)`. |
| 103 | + - Resolve via `MainInstaller.Resolve<IMyService>()` or `TryResolve`. |
| 104 | + - Clear bindings on reset via `MainInstaller.Clean()` (or `Clean<T>()` / `CleanDispose<T>()`). |
| 105 | +- **Update versioning** |
| 106 | + - Ensure `version-data.txt` exists/updates correctly in `Assets/Configs/Resources/`. |
| 107 | + - If changing `VersionServices.VersionData`, update both runtime parsing and `VersionEditorUtils` writing logic. |
| 108 | + |
| 109 | +## 8. Update Policy |
| 110 | +Update this file when: |
| 111 | +- The binding/service-locator API changes (`Installer`, `MainInstaller`) |
| 112 | +- Core service behavior changes (publish safety rules, tick timing, coroutine completion/cancellation semantics, pooling lifecycle) |
| 113 | +- Versioning pipeline changes (resource filename, editor generator behavior, runtime parsing) |
| 114 | +- Dependencies change (`package.json`, new external types like `floatP`) |
| 115 | + |
0 commit comments