Skip to content

Release 2.0.0#30

Merged
CoderGamester merged 17 commits intomasterfrom
develop
Apr 26, 2026
Merged

Release 2.0.0#30
CoderGamester merged 17 commits intomasterfrom
develop

Conversation

@CoderGamester
Copy link
Copy Markdown
Owner

New:

  • Added Services Explorer window (Tools > GameLovers > Services Explorer) with 13 live-refresh tabs: Overview, Installer, MessageBroker, Tick, Coroutine, Pool, Data, Time, RNG, AssetResolver, Versioning, Assets Importer, Addressable Ids — works in both Edit and Play mode
  • Menu stubs under Tools > GameLovers:
    • Versioning / Refresh Version Data and Versioning / Open in Explorer
    • Assets Importer / Import Assets Data and Assets Importer / Open in Explorer
    • Addressable Ids / Generate Addressable Ids and Addressable Ids / Open in Explorer
  • Added Assets > Create > GameLovers Services > … scaffolders: Message, Command, Service, Pool Entity (template-based, $NAME$ / $NAMESPACE$ substitution)
  • Absorbed com.gamelovers.assetsimporter v0.5.2 into this package
  • Added IAssetLoader, ISceneLoader, AddressablesAssetLoader to Runtime/AssetsImporter/ (ns GameLovers.Services.AssetsImporter)
  • Added AddressableConfig, AssetConfigsScriptableObject<TId,TAsset>, AssetLoaderUtils, AssetReferenceScene to Runtime/AssetsImporter/ (ns GameLovers.Services.AssetsImporter)
  • Added AssetResolverService (implements IAssetResolverService / IAssetAdderService) to Runtime/ root (ns GameLovers.Services)
  • Added Editor/AssetsImporter/: AssetsImporter, AssetsToolImporter, AssetConfigsImporter, AddressableIdsGenerator, AddressablesIdGeneratorSettings (ns GameLovers.Services.AssetsImporter.Editor)
  • Added importable Samples under Samples~/ (importable via Unity Package Manager > GameLovers Services > Samples).
    • Services Playground — single-scene, zero-setup walk-through that wires every foundation service via MainInstaller and exercises 10 of 13 Services Explorer tabs end-to-end.
    • Asset Resolver — focused demo of AssetResolverService end-to-end (AddConfigs / RequestAsset / UnloadAssets) with SpriteConfigs : AssetConfigsScriptableObject<SpriteId, Sprite>. Drives the three Services Explorer tabs the Playground does not cover (Asset Resolver, Assets Importer, Addressable Ids).

Changed:

  • Addressable Ids generator and Assets Importer settings moved from Assets/*.asset ScriptableObjects to ProjectSettings/ ScriptableSingletons (mirrors VersioningEditorSettings).
  • Generation logic extracted from AddressableIdsGenerator into AddressableIdsGeneratorUtils (static internal); importer discovery/import logic extracted into AssetsImporterEditorUtils.
  • Tools/Assets Importer/* and Tools/AddressableIds Generator/* menu entries removed. Use Tools/GameLovers/Assets Importer/..., Tools/GameLovers/Addressable Ids/..., or the Services Explorer tabs instead.
  • Toggle Auto Import On Refresh menu entry removed. The toggle now lives exclusively in the Services Explorer Assets Importer tab.
  • Assets/AssetsImporter.asset and Assets/AddressablesIdGeneratorSettings.asset are no longer used (settings moved to ProjectSettings/); safe to delete from consumer projects.
  • Deleted editor source files: AssetsImporter.cs, AssetsToolImporter.cs, AddressableIdsGenerator.cs, AddressablesIdGeneratorSettings.cs.
  • Folder reorganization: Runtime/ now has domain subfolders DependencyInjection/, Commands/, Pooling/, AssetsImporter/; Editor/ now has Versioning/ and AssetsImporter/ subfolders
  • Installer.cs and MainInstaller.cs moved to Runtime/DependencyInjection/ (namespace unchanged: GameLovers.Services)
  • CommandService.cs trimmed to concrete class only; command contract interfaces extracted to Runtime/Commands/ under ns GameLovers.Services.Commands
  • PoolService.cs trimmed to concrete class only; pool interfaces + implementations moved to Runtime/Pooling/ under ns GameLovers.Services.Pooling
  • ObjectPool.cs (578 lines, 10 types) split into 4 files under Runtime/Pooling/: IPoolEntity.cs, IObjectPool.cs, ObjectPool.cs, GameObjectPool.cs
  • VersionEditorUtils.cs and GitEditorProcess.cs moved to Editor/Versioning/; re-namespaced from GameLovers.Services.EditorGameLovers.Services.Versioning.Editor
  • Added new hard dependencies: com.unity.addressables (1.21.20) and com.cysharp.unitask (2.5.10)

Fixed:

  • AddressablesAssetLoader.UnloadAsset no longer calls GC.Collect(), GC.WaitForPendingFinalizers(), or Resources.UnloadUnusedAssets(). The method now only decrements the Addressables reference count. The old implementation caused PlayMode Test Runner crashes on macOS and O(total-assets-in-memory) main-thread stalls per per-asset release. Callers that need memory reclamation should invoke Resources.UnloadUnusedAssets() themselves at appropriate moments (scene transitions, boot, memory-pressure events); Unity also runs an unused-assets sweep automatically on LoadSceneMode.Single scene loads.
  • Corrected IAssetLoader.UnloadAsset XML documentation: removed the incorrect "will also destroy GameObject instances" claim — Addressables.Release(gameObject) does not destroy the instance; callers must Object.Destroy it separately.
  • IAsyncCoroutine.StopCoroutine(bool triggerOnComplete) now honors its triggerOnComplete parameter and flips IsCompleted to true and IsRunning to false after stopping. The previous implementation always invoked OnComplete callbacks regardless of the flag and left state flags unchanged, so consumers could not distinguish a stopped coroutine from a running one and triggerOnComplete: false was silently ignored.
  • GameObjectPool.Dispose() and GameObjectPool<T>.Dispose() now skip pooled entries whose underlying GameObject has already been destroyed by an external owner (e.g. a parent GameObject was destroyed while pooled instances were still reparented under it via DespawnToSampleParent).

Breaking Changes — see MIGRATION.md for details:

  • Pool types moved from GameLovers.Services to GameLovers.Services.Pooling (IPoolService, IObjectPool, IObjectPool<T>, ObjectPool<T>, ObjectPoolBase<T>, GameObjectPool, GameObjectPool<T>, IPoolEntitySpawn, IPoolEntitySpawn<T>, IPoolEntityDespawn, IPoolEntityObject<T>). PoolService concrete class remains in GameLovers.Services.
  • Command contract types moved from GameLovers.Services to GameLovers.Services.Commands (IGameCommandBase, IGameCommand<>, IGameServerCommand<>, ICommandService<>). CommandService<> concrete class remains in GameLovers.Services.
  • GameLovers.AssetsImporter.* renamed to GameLovers.Services.AssetsImporter.*
  • GameLovers.AssetsImporter.AssetResolverService is now GameLovers.Services.AssetResolverService
  • GameLovers.Services.Editor.* (versioning editor) renamed to GameLovers.Services.Versioning.Editor.*
  • IAssetLoader.UnloadAssetAsync<T>(T, Action)IAssetLoader.UnloadAsset<T>(T, Action): method renamed (dropped Async suffix) and return type changed from UniTask to void to reflect its synchronous nature. Replace await loader.UnloadAssetAsync(x); with loader.UnloadAsset(x);.
  • Code generated by AddressableIdsGenerator must be re-generated (updated emitted using statement)

CoderGamester and others added 17 commits January 21, 2026 15:04
- Fixed 7 API inaccuracies in README code examples (TickService, DataService,
  CommandService, CoroutineService, MessageBroker, dependency name, version badge)
- Added IDataProvider, ITimeManipulator, ICommandService<TGameLogic> to Key Components
- Added CommandService section to AGENTS.md; corrected TickService/CoroutineService
  method names; documented IDataProvider/ITimeManipulator split; added test structure
- Removed .github/workflows/openai.yml CI workflow

Made-with: Cursor
AGENTS.md:
- Add interface-to-concrete lookup table (Section 2.5)
- Document Bind<T,T1,T2,T3> and re-binding-throws behavior
- Add bulk UnsubscribeAllOnUpdate/FixedUpdate/LateUpdate overloads
- Add test coverage gap map (Section 3.5)
- Add error quick-reference table to Section 4

README.md:
- Add StopCoroutine(triggerOnComplete) ignored-flag caveat
- Add GameObjectPool<T> requires T : Behaviour constraint note
- Add GetData<T> KeyNotFoundException behavior to Key Points
- Add Unsubscribe(action) and StopAllCoroutines/Dispose to examples
- Soften platform compatibility claims to reflect actual test coverage

Tests:
- Fix misleading UnsubscribeAll_BySubscriber test (was testing wrong overload)
- Fix commented-out EntityDespawn_Successfully (NSubstitute mock needed Returns(true))
- Add StartDelayCall and StartDelayCall<T> tests (CoroutineService)
- Add SubscribeOnFixedUpdate/LateUpdate and their unsubscribe tests (TickService)
- Add SaveAllData round-trip test (DataService)
- Add Clear, Dispose, SpawnWithData tests (PoolService)
- Add IsSpawned, Despawn(Func), SpawnedReadOnly, SampleEntity tests (ObjectPool)
- Add Bind<T,T1,T2> and Bind<T,T1,T2,T3> tests (Installer)
- Add Nextfloat, Peekfloat, Range(floatP,floatP) tests (RngService)
- Add SpawnWithData test (GameObjectPool)
- Add GameObjectPoolTypedTest.cs -- first tests for GameObjectPool<T>

Made-with: Cursor
- ObjectPool<T>: add Reset and Despawn(bool:all, Func) tests
- RngService: add PeekRange(floatP,floatP) no-state-advance test
- TickService: add UnsubscribeAllOnUpdate/FixedUpdate/LateUpdate (no-arg + subscriber-scoped, 6 tests)
- CoroutineService: add Dispose host-GameObject teardown and double-dispose safety tests
- GameObjectPool<T>: add DespawnAll, SampleEntity, SpawnedReadOnly, IsSpawned,
  Despawn(bool,Func), Reset, DespawnToSampleParent, Dispose(bool) tests
- VersionServices: new PlayMode integration test covering LoadVersionDataAsync,
  VersionInternal, Branch, Commit, BuildNumber, and pre-load throw behaviour
- AGENTS.md: remove section 3.5 (all gaps now covered); update test directory listing

Made-with: Cursor
Documents the full test suite conventions for this package: EditMode vs
PlayMode placement rules, namespace and naming patterns, mock/helper type
setup, assertion style, performance test structure, and integration test
ordering — so agents and contributors can write consistent tests without
reverse-engineering the existing suite.

Made-with: Cursor
- Extract Section 5 (Testing Conventions) from root AGENTS.md into a
  dedicated Tests/AGENTS.md, following the sub-folder AGENTS.md pattern.
- Add MUST-read pointer to Tests/AGENTS.md in Section 3 of root AGENTS.md.
- Rename "Dev Workflows" → "Package Dev Workflows" to clarify contributor
  scope; remove consumer-facing "Bind/resolve services" workflow (already
  documented in README.md).
- Renumber sections 5–8 accordingly; move test-conventions update trigger
  to Tests/AGENTS.md update policy.

Made-with: Cursor
Reorganizes Runtime/ and Editor/ into domain subfolders with matching
sub-namespaces (except DependencyInjection/ which stays as a navigation-only
carve-out). Absorbs all asset-loading/importing functionality previously in
com.gamelovers.assetsimporter.

Runtime/
- DependencyInjection/ -> ns GameLovers.Services (carve-out)
- Commands/            -> ns GameLovers.Services.Commands  (IGameCommand*, ICommandService)
- Pooling/             -> ns GameLovers.Services.Pooling   (IPoolService, IObjectPool*, GameObjectPool*, IPoolEntity*)
- AssetsImporter/      -> ns GameLovers.Services.AssetsImporter (IAssetLoader, ISceneLoader, AddressablesAssetLoader, AddressableConfig, AssetConfigsScriptableObject, AssetLoaderUtils, AssetReferenceScene)
- *Service.cs + VersionServices.cs + AssetResolverService.cs at root -> ns GameLovers.Services

Editor/
- Versioning/      -> ns GameLovers.Services.Versioning.Editor
- AssetsImporter/  -> ns GameLovers.Services.AssetsImporter.Editor
- New explicit asmdef: GameLovers.Services.Editor.asmdef (refs Addressables + Addressables.Editor + ScriptableBuildPipeline.Editor)

Runtime asmdef:
- rootNamespace = "GameLovers.Services"
- Added GUID refs: Unity.Addressables, Unity.ResourceManager, UniTask, UniTask.Addressables

File splits:
- CommandService.cs trimmed to concrete class; interfaces moved to Commands/
- PoolService.cs trimmed to concrete class; IPoolService moved to Pooling/
- ObjectPool.cs (10 types) split into 4 files under Pooling/

Tests:
- New EditMode/Unit: AddressableConfigTest, AssetLoaderUtilsTest, AssetResolverServiceTest
- New PlayMode/Integration: AddressablesAssetLoaderIntegrationTest (marked [Explicit])
- Existing pool/command tests updated with new sub-namespace usings
- Tests asmdefs extended with Addressables + UniTask + ResourceManager refs

Documentation:
- package.json: version 2.0.0; +com.unity.addressables 1.21.20; +com.cysharp.unitask 2.5.10
- AGENTS.md: updated Interface-to-Concrete table, folder layout, namespace mapping, gotchas
- Tests/AGENTS.md: added new test files under Section 9
- README.md: new "Asset Loading & Import" section; bumped version badge
- CHANGELOG.md: [2.0.0] entry documenting all changes and breaking items
- MIGRATION.md: new file with before/after `using` diffs for every breaking namespace change

BREAKING CHANGES — see MIGRATION.md:
- Pool types now in GameLovers.Services.Pooling
- Command contract types now in GameLovers.Services.Commands
- GameLovers.AssetsImporter.* renamed to GameLovers.Services.AssetsImporter.*
- GameLovers.AssetsImporter.AssetResolverService now in GameLovers.Services
- GameLovers.Services.Editor (versioning) renamed to GameLovers.Services.Versioning.Editor
- Re-run AddressableIdsGenerator or manually update the emitted `using` in generated files

Made-with: Cursor
… NSubstitute limitation

- Qualify Object.DestroyImmediate as UnityEngine.Object.DestroyImmediate in
  AssetResolverServiceTest to resolve CS0104 ambiguity between System.Object
  and UnityEngine.Object.
- Add two regression tests in ObjectPoolTest for Despawn(bool, Func<T,bool>)
  covering distinct-instance matches and partial matches. The existing
  Despawn_WithCondition_AllMatching_DespawnsAll only exercises duplicate
  references because the SetUp factory returns a single mock instance.
- Document in Tests/AGENTS.md the NSubstitute 4.4.0 + Castle DynamicProxy
  proxy-generation failure on Unity's Mono runtime for self-referentially
  constrained generic interfaces (e.g. IObjectPool<T> where T :
  IPoolEntityObject<T>); prescribe real implementations or hand-written
  fakes as the fix.

Made-with: Cursor
- Add CLAUDE.md + .meta at package root; thin @AGENTS.md import wrapper
- Trim README.md to Option B shape (~700 -> 279 lines): lean Why / Install /
  Quick Start / Services-at-a-Glance / Related docs; dynamic version badge
- Move deep per-service API reference into docs/ (11 topics + README index):
  main-installer, message-broker, tick-service, coroutine-service,
  pool-service, data-service, rng-service, time-service, command-service,
  version-services, asset-loading
- AGENTS.md: add Companion-files blockquote; Mermaid architecture diagram
  in Section 2; Tests "what lives where" table; consolidate editor-only
  bullets in Section 3

Made-with: Cursor
…itor tooling (v2.0.0)

- Added Services Explorer window (13 live-refresh tabs: Overview, Installer, MessageBroker, Tick, Coroutine, Pool, Data, Time, RNG, AssetResolver, Versioning, Assets Importer, Addressable Ids)
- Added Assets > Create > GameLovers Services scaffolders (Message, Command, Service, Pool Entity templates)
- Added Editor/Inspectors: AssetConfigsScriptableObjectEditor, AssetReferenceSceneDrawer
- Extracted generation logic into AddressableIdsGeneratorUtils and AssetsImporterEditorUtils; replaced old AssetsImporter.cs / AssetsToolImporter.cs / AddressableIdsGenerator.cs / AddressablesIdGeneratorSettings.cs
- Added AssetsImporterEditorSettings and AddressableIdsEditorSettings ScriptableSingletons under ProjectSettings/
- Added menu stubs under Tools > GameLovers: Versioning/*, Assets Importer/*, Addressable Ids/*
- Moved VersionEditorUtils/GitEditorProcess to Editor/Versioning/; added VersioningEditorSettings + VersioningMenu
- Added AssemblyInfo.cs to both Runtime/ and Editor/
- Condensed AGENTS.md; updated CHANGELOG.md, MIGRATION.md, README.md, docs/; added .meta files for docs/
- Replaced AddressablesAssetLoaderIntegrationTest with unit-level AddressableIdsEditorSettingsTest, AssetsImporterEditorSettingsTest, VersioningEditorSettingsTest

Made-with: Cursor
Ships two importable samples under Samples~/ via Package Manager:

- ServicesPlayground: zero-setup scene exercising 10 of 13 Services
  Explorer tabs (every foundation service except the asset pipeline).
  Hand-authored ServicesPlaygroundUI.prefab with 27 buttons across 10
  sections + scrollable Log + scrollable LiveStatus + a programmatically-
  spawned bullet pool that auto-despawns off-screen entries.

- AssetResolver: Addressables-backed demo of AssetResolverService end-to-
  end (AddConfigs / RequestAsset / UnloadAssets) using a SpriteConfigs :
  AssetConfigsScriptableObject<SpriteId, Sprite>. Hand-authored prefab
  UI; per-sample README walks through the ~2-min Addressables setup.

Both drivers wire Button.onClick listeners via [SerializeField] +
AddListener in Awake, swap StandaloneInputModule <-> InputSystemUIInput-
Module at runtime via #if ENABLE_INPUT_SYSTEM, and use TextMeshProUGUI
for cross-Active-Input-Handling and font portability. Sample-only types
live under GameLovers.Services.Samples.<sample> namespaces to make the
public-API boundary explicit.

Also fixes a latent infinite-recursion crash in
AssetConfigsScriptableObjectEditor.CreateInspectorGUI: with editor-
ForChildClasses=true, calling new InspectorElement(serializedObject)
re-resolved to the same custom editor and recursed ~5,300 levels deep
until the OS stack guard killed the editor with EXC_BAD_ACCESS on
selection of any concrete subclass. Replaced with manual
SerializedProperty iteration via PropertyField. The bug had been
latent since no concrete AssetConfigsScriptableObject<,> subclass
existed in the repo until the new sample suite added SpriteConfigs.

Documentation aligned: package.json declares samples[]; README.md gets
a Samples section; AGENTS.md §3 documents Samples~/ and §4 adds the
Sample Zero-Setup Invariant gotcha + the IAssetAdderService default-
interface-method gotcha; CHANGELOG.md gains an [Unreleased] entry.
Runtime fix in CoroutineService.AsyncCoroutine:
- StopCoroutine(triggerOnComplete) now honors its parameter and flips
  IsCompleted=true / IsRunning=false (no-op if already completed). The
  previous implementation always invoked OnComplete and never updated state.
- Added internal InternalCleanup event distinct from the public _onComplete
  field. Editor-only _activeAsyncCoroutines tracking subscribes via
  InternalCleanup so it no longer overwrites (or is overwritten by) user
  OnComplete callbacks - the v2.0.0 regression that made StartDelayCall
  callbacks silently fail in editor builds and stale entries persist after
  per-row Stop in the Coroutine tab.

Services Explorer lifecycle hardening:
- New ServiceTab.OnExitingPlayMode() virtual + !EditorApplication.isPlaying
  short-circuit at the top of every populated-state tab's Refresh().
  Decouples the Explorer's empty state from MainInstaller cleanup so tabs
  visually clear the moment Stop is pressed regardless of whether the
  consumer's bootstrap properly disposed services or called Clean().
  Adopted by 7 tabs: Installer, MessageBroker, Data, RNG, Pool, Coroutine,
  Tick. Banner is now context-sensitive ("Not in Play mode - showing last
  snapshot" before first play, "Play session ended - services unbound"
  after).

Services Explorer UX:
- Added .action-primary-danger USS class + MakePrimaryDangerButton helper
  for destructive primary actions (Stop All Coroutines, Unsubscribe All,
  Clean All). Rewrote .row-btn-danger from text-color-only to tinted-bg
  + border + bold light-pink to fix contrast in both editor skins.
- MessageBrokerTab redesign: replaced freeform "Type FQN" TextField with a
  reflection-populated DropdownField over IMessage implementers, split
  Test Publish from the action bar, moved per-type Unsubscribe All onto
  the Foldout's Toggle header (with evt.StopPropagation() to prevent
  collapse-on-click).
- RngTab UX rebuild: always-on Next-value row, tooltips, "Restore" renamed
  to "Set counter" + Rewind-to-0 shortcut, cramped IntegerFields replaced
  with Slider+IntegerField pairs (matches TimeTab.AddTime layout).

Sample (ServicesPlayground):
- Bullet material now self-illuminates via _EmissionColor + _EMISSION
  keyword + globalIlluminationFlags=None so the pool demo is readable in
  the empty playground scene without scene lights.
- Coroutine_StartAsync uses WaitForSeconds(3f) instead of WaitFrames(60)
  so the handle spans multiple Coroutine-tab refresh cycles at high
  editor framerates.
- Removed the "Reset spawn counts" button entirely - it only reset two
  sample-internal static counters and demonstrated nothing about
  IPoolService. Removal spans script field + handler + Bullet.ResetCounters
  + 10 prefab YAML documents + parent's m_Children entry + 6 stale scene
  PrefabModification overrides.

Tests: added 6 PlayMode tests covering the new StopCoroutine contract,
the OnComplete-no-overwrite regression, and StopCoroutine idempotency.

Docs: AGENTS.md updated with Services Explorer Play->Edit Refresh
Lifecycle, Destructive-Action Styling, and two new sample-zero-setup
invariants (bullet emission, coroutine-timing). The IAsyncCoroutine.
StopCoroutine(triggerOnComplete) "ignored parameter" gotcha is replaced
with the new honored-contract documentation.

Made-with: Cursor
GameObjectPool.Dispose() and GameObjectPool<T>.Dispose() iterate the
pool's internal Stack and call Object.Destroy on each entry. When a
pooled instance had been reparented under an external GameObject (e.g.
via DespawnToSampleParent) and that parent was destroyed by the owner,
the pool still tracked the now-destroyed entry; Dispose then either
called Destroy on a Unity fake-null (harmless) or, in the typed pool,
dereferenced .gameObject on a destroyed Behaviour and threw
MissingReferenceException.

Both Dispose paths now use the same Unity fake-null guard
(`if (obj == null) continue;`) that SpawnEntity already uses, mirroring
the package's existing convention for iterating pool internals.

Adds two PlayMode regression tests (one per pool variant) and an
"External destruction resilience" gotcha to AGENTS.md so future code
paths that iterate pool internals get the same treatment.

Made-with: Cursor
Eliminates the previous ~2-minute manual setup (mark sprites Addressable
+ populate SpriteConfigs.asset entries) that the per-sample README
documented as four user-facing steps. The sample now ships placeholder
sprites (Hero/Coin/Enemy.png) and an editor automation that performs
all wiring on import.

What runs:
- AssetResolverSampleAssetPostprocessor fires when any asset under
  /Asset Resolver/Sprites/ is imported, defers to delayCall (modifying
  assets during OnPostprocessAllAssets is unsafe).
- An [InitializeOnLoadMethod] safety net handles the chicken-and-egg of
  first-time UPM sample import where the post-processor would miss the
  initial batch (sprites import before this script compiles).
- Steps performed (idempotent): rename non-canonical filenames to
  Hero/Coin/Enemy (substring match first, alphabetical fallback),
  get-or-create the dedicated Addressables group
  GameLoversServicesSamples_AssetResolver, mark each sprite Addressable
  in that group with a clean lowercase address, and wire SpriteConfigs.asset
  rows for matching SpriteId values. Existing user mappings to a
  different sprite are respected.

Manual escape hatches:
- Menu: Tools > GameLovers > Samples > Asset Resolver > Refresh Addressables.
- A sample-scoped "Refresh AssetResolver Sample Addressables" button on
  the SpriteConfigs.asset inspector. The button is added by the
  package's AssetConfigsScriptableObjectEditor only when the inspected
  asset's path ends with "/Asset Resolver/SpriteConfigs.asset", and is
  invoked through EditorApplication.ExecuteMenuItem so the inspector
  stays decoupled from the sample's editor assembly. If the sample's
  scripts are missing, the menu invocation fails with a logged warning
  rather than a compile error.

Sample assembly:
- New asmdef GameLovers.Services.Samples.AssetResolver.Editor scoped to
  Editor platform; lives entirely under Samples~/, so removing the
  sample removes the automation (no orphan InitializeOnLoad in
  consumer projects).

Docs:
- Samples~/AssetResolver/README.md rewritten around the auto-setup flow
  with a "swap in your own sprites" section and a manual fallback flow
  for users who prefer the original four-step path.
- Samples~/README.md updated: file tree gains Sprites/ + Editor/, and
  the index summary changes from "~2 minutes of one-time setup" to
  "Zero per-import setup".
- package.json sample description updated.

Made-with: Cursor
Unity's Performance Testing Package consumes two PlayerPref keys in
EditMode:
  - PT_Run      (run metadata; consumed when results are emitted)
  - PT_Settings (RunSettings; consumed by MethodMeasurement.SettingsOverride()
                 *before* the first warmup runs)

PerformanceTestSetup.InitializePerformanceTestMetadata() previously
primed only PT_Run, leaving RunSettings.Instance to lazy-load from an
empty PlayerPref string. JsonUtility throws on the empty input,
ResourcesLoader silently swallows the exception and returns null, and
SettingsOverride() then NRE's on RunSettings.Instance.MeasurementCount
at MethodMeasurement.cs:288 — before any warmup. The failure surfaced
as a generic NullReferenceException with no hint that the harness was
incomplete; every EditMode [Test, Performance] fixture in the suite
(ObjectPoolPerformanceTest, MessageBrokerPerformanceTest) was affected.

Now primes PT_Settings with `{"MeasurementCount":-1}`. -1 is the
package's "no override" sentinel: SettingsOverride() early-returns when
count < 0, preserving each fixture's per-test
.WarmupCount(...).MeasurementCount(...) configuration.

The previous misleading inline comment ("only Run metadata is required")
is replaced with a structured <remarks> XML doc that names which call
chain consumes which key, so a future contributor cannot accidentally
regress to the single-key form.

Adds PerformanceTestSetupTest.MeasureMethod_AfterInitialize_DoesNotThrow
as a regression sentinel: a no-op
Measure.Method(() => {}).WarmupCount(1).MeasurementCount(1).Run()
wrapped in Assert.DoesNotThrow. The class name targets the harness, not
a service, so a future failure points directly at PerformanceTestSetup.

Tests/AGENTS.md gains a "PerformanceTestSetup PlayerPref contract"
section in §7 documenting the two-key contract, the failure mode, and
the role of the sentinel test.

Made-with: Cursor
…Resolver full coverage

- AddressableIds tab: persisted last-generation snapshot in
  AddressableIdsEditorSettings (sorted address/label arrays + timestamp
  + filename/label-filter used). New cheap-tier ComputeFreshness
  (file-stat only) drives a color-coded freshness banner on every
  Refresh; new expensive-tier Diff (GetAssetList + ProcessData) runs
  only on the user's "Check for stale Ids" button click and renders
  added/removed/warning lists in a Foldout.
- Services Explorer hardening: ServiceTab gains MakeStickyFoldout (per-
  tab persisted collapsed state, with evt.target == foldout filter to
  block ChangeEvent<bool> bubbling through nested foldouts) and
  TryShortCircuitRefresh (digest-based early-out to avoid destroying
  mouse-captured VisualElements mid-click). AssetResolverTab and
  MessageBrokerTab adopt both; AssetResolverTab's destructive toggle
  invalidates the digest on change.
- AssetResolver sample now exercises all three Explorer tabs the
  ServicesPlayground does not cover: AssetResolverExample binds
  IAssetResolverService through MainInstaller (populates Asset
  Resolver tab live); new SpriteConfigsImporter surfaces a sample
  row in the Assets Importer tab; post-processor applies
  'services-sample-asset-resolver' label so Addressable Ids tab can
  demo a sample-scoped Generate. Adds a sample-runtime asmdef + an
  in-scene "Open Services Explorer" button that jumps to the
  AssetResolver tab via a sample-scoped editor menu.
- Confirmed the AssetResolver "removing the dedicated group is the
  user's undo" design after a self-cleanup attempt was found
  structurally impossible (Unity recompiles before firing
  OnPostprocessAllAssets for deletions, dropping the post-processor
  class itself). Documented in AGENTS.md so the dead pattern is not
  re-attempted.

Folds all of the above into the unreleased ## [2.0.0] CHANGELOG entry
per the pre-publication versioning policy.

Made-with: Cursor
@CoderGamester CoderGamester merged commit 18cfd93 into master Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant