Class ContentDirectoryStore
The content-directories provider: registers built content directories, resolves a key to a Unity.Loading.Loadable<T>, and loads it.
This is the same job the Addressables address table does for AssetLibrary, so it lives behind
that same door. It is not a second door: AssetLibrary is still the only place the rest of the game asks
for an asset.
Facts this type is built on, all measured in this project (2026-09-19, see
GrindFest.Workspace/SYNC_LOAD_ELIMINATION_PLAN.md §2.5):
- Registration reads the build manifest and the root assets into memory (3.7 ms for a small directory;
19.2 ms in a browser) — it is **not** residency. Assets load on
Load()(blocking) orLoadAsync(). - Search order across directories is **reverse registration**, so a mod registered after the base game wins. That is the mod-over-base override, for free.
- **A build name can be registered only once per session** — registering a second copy of the same name fails — so unregistering has to precede reloading a mod.
- Unity.Loading.Loadable<T> holds **one** reference: the first
Releaseunloads for everyone. So a caller must not share one; this type keeps the id and hands out a per-callerLoadable. - A content directory is built for the **active build target**, and registration requires **play mode** in the editor ("not supported in edit mode").
public static class ContentDirectoryStore
- Inheritance
-
objectContentDirectoryStore
Properties
- AuthoringKeyBuildCount
How many times the authored key set has been (re)built — diagnostics, and a thing tests can assert.
- AuthoringOnlyKeyCount
How many keys the authoring source contributes that no built directory has — exactly the set a rebuild of the content directory would add, which makes it the honest measure of "the build is behind the project".
- HasAuthoringSource
Whether an authoring source (editor) is registered.
- IsAvailable
True once at least one directory with roots has been registered, or an authoring source is wired. The editor answers true before any build, which is the point: it is what "content is available" means there. Deliberately does not force the lazy key scan.
- KeyCount
How many keys the registered directories and the authoring source can serve between them.
- Keys
The keys the registered directories and the authoring source can serve together — for tests, diagnostics and name→address resolution. Order is directories first, then the authoring-only remainder, so a caller that takes the first match keeps preferring what actually ships.
Methods
- ClearAuthoringSource()
Forgets the authoring source and its keys (used when the editor leaves play mode or rescans).
- IsRegistered(string)
Whether this exact path is one of the registered directories.
- KeysFor(string)
The keys one registered directory contributed, or an empty list when it is not registered.
- LoadAsync(string)
The async form — the default for anything that can wait.
- RegisterAuthoringKeyProvider(Func<IReadOnlyCollection<string>>)
Registers how the authoring source enumerates its keys. Called lazily — see GrindFest.Content.ContentDirectoryStore.EnsureAuthoringKeys() — because enumeration is only needed to resolve a name to an address, and for diagnostics, while single-key lookups go straight through the resolver.
- RegisterAuthoringSource(Func<string, Object>)
Registers the editor's authoring source: a delegate that turns an address into the project's own asset, so keys resolve from the AssetDatabase instead of from a built content directory.
Called from the editor assembly (see
Editor/ContentPipeline/ContentAuthoringSource.cs), which is why it takes a delegate rather than calling AssetDatabase itself. Deliberately has no play-mode guard: the key set can be prepared in edit mode by tooling, and only the delegate is exercised in play mode.
- Release(string)
Gives the store's handle back. The caller count is AssetLibrary's business.
- ReleaseAll()
Unregisters everything, releasing what is open first — order matters or Unity logs.
- SetAuthoringKey(string, bool)
Adds or removes one authored key — the increment an import callback makes, so a new prefab is visible to the next lookup without rescanning the project.
- SetAuthoringKeys(IEnumerable<string>)
Replaces the authored key set after a full (re)scan. Kept whole-set rather than per-key because that is what the scan produces, and a key set that is half-old and half-new is the one state nobody can reason about.
- TryLoad(string, out Object)
Blocking load. Legal on every platform (that is the point of content directories) but it is a frame stall, so it stays reserved for values consumed as data — the same rule the plan states for a synchronous read: the value must be needed as data, and its content must already be inside a registered scope.
- TryRegister(string, List<string>)
Registers a built content directory. Returns false (with the reason logged) when it cannot be used, so a caller can decide whether that is fatal instead of getting an exception out of the loading path.
- Unregister(string)
Unregisters a directory and forgets its keys. Callers that still hold assets must Release first.