Strata: Layered Data & Asset Loading System #584
Rongmario
started this conversation in
Work in Progress
Replies: 2 comments 1 reply
|
This proposal aims to deliver on and perhaps |
0 replies
|
Is the future tag system planned to be data-driven? |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Summary
A runtime-reloadable subsystem that loads both server-side data (
data/) and client-side assets (assets/) from an ordered stack of layered sources such as mod JARs, resource packs, and the active world.Goals
data/and clientassets/flow through the same source model, the same IO layer, and the same cached indexes./reloadcommand) with per-consumer failure isolation, atomic swap of baked results, and a completion event for cache invalidation. Previous point also means that:/reloadshould never freeze the tick loop.SimpleReloadableResourceManagerbecomes a deprecated adapter over Strata with identical class, constructor, and method signatures, so the existing 1.12 mod ecosystem keeps working unmodified... including reload listeners and Forge's selective reload.Non Goals
DataResult, noDynamicOps, no dynamic backend plugging.IResource,IResourceManager,IResourcePack,SimpleResource, the pack repository, and the metadata (.mcmeta) system all remain untouched.Motivation
The 1.12
OreDictionaryis the only widely-used "shared classification" mechanism, and it isItemStack-only, mutation-based, not reloadable, and not data-driven. Modernizing it touches on file discovery, layered merging, override/removal semantics, attribution, runtime reload, and client sync. Every one of those concerns is generic and this CFP serves to cover it.1.12 has no meme version "datapack" infrastructure: there is no server-side resource manager, so data cannot ride any existing system. Even worse is that every feature that reads files out of mod JARs reinvents JAR walking from scratch, just take a look at :
CraftingHelper.findFilesfor example... Opens and closes a zipFileSystemper mod per walk. Strata aims to solve the plumbing once, with an IO layer that maps each JAR exactly once per run and shares that index between the data and asset sides.On the client,
SimpleReloadableResourceManager/FallbackResourceManagerre-enumerate packs eagerly per domain and predate every modern IO facility. Since Strata indexes every mod JAR anyway, making it the real backend behind the vanilla client resource system removes the duplication instead of adding a parallel one.Description
The system is Strata. Its unit of contribution is a stratum: a single source's root. A mod ships a stratum (the
data/andassets/roots of its JAR), a resource pack ships a stratum (assets only), the active world ships a stratum (<world>/strata/, data only). The ordered stack of all active strata forms the layering that determines override precedence.Source model
One stratum exists per physical source (deduplicated), and both the data stack and the assets stack view the same cached file indexes. A mod may declare its priority in some optional way in their jars. Layers will see what preceded them and opt to manipulate by overriding, merging etc.
File discovery
For each data stratum, Strata walks:
<namespace>: modid of the data. Independent of which source provided it, any stratum may write to any namespace.<category>: which consumer receives the file (e.g.tags). Routes the file to a registered consumer.Discovery is per-stratum, so each consumer receives files grouped and ordered by source layer. Enumeration is a prefix-range query against the source's sorted name index, no need to walk directories or reopen zips.
Consumer SPI
Features register a consumer during a bootstrap event fired once at load completion. Because registration is code present identically on client and server, the consumer set is deterministic across sides which is the prerequisite for safe synchronization.
Something like:
Each
StrataEntrycarries the file'sResourceLocationid, its already-decoded valueE, and the stratum it came from, so the consumer can implement its own override/merge semantics with full attribution.Scribes
A scribe is a typed schema that does the work: it reads JSON (or any data type that has valid serder backends) from disk and writes/reads the network form for its type. Consumers declare scribes by explicit composition over record classes. The canonical record constructor is resolved once via a caller-supplied
MethodHandles.lookup(), so there is no per-parse reflection:For example:
MethodHandleinvocation constructs the record. Unknown keys are skipped, duplicates are last-wins with the aforementioned logging, and missing required fields produce errors attributed to source and file, perhaps even byte offsets...JsonValuetree remains available as an escape hatch for fully dynamic consumers.IO layer
Each zip source is memory-mapped once per run as a single
MemorySegment(FFM -FileChannel.map). Its central directory is parsed once into a sorted name table supporting exact lookup and prefix-range enumeration. Stored entries are served as true zero-copy slices - deflated entries are inflated withThreadLocalInflatersdirectly from the mapped segment. A process-wide cache keyed onpath,size, andmtimeshares these indexes between the data and asset sides and invalidates stale files on reload. Directory sources (dev workspaces, folder packs, the world stratum) read files conventionally, mmap of many small files is syscall churn for nothing. ZIP64 archives fall back tojava.util.zip.A custom pull-mode JSON scanner operates directly over memory segments with no intermediate DOM and no Gson in the hot path will easily win out the old performance w/ Gson, it can also parse errors carrying the source id, stratum, and byte offset.
Reload lifecycle
/reload. 1.12 has no vanilla/reload; Strata introduces it. The command replies immediately and reports completion when apply lands. The tick loop never blocks on IO or parsing.Attribution
Each contribution is stamped with its source stratum. Strata does not retain a full in-memory audit trail. Instead, whenever a source overrides or removes another source's contribution, logging is emitted for both sources.
Sync
Consumers with a non-null
syncScribe()are synchronized automatically: on player login and after each reload, the baked result is serialized with the scribe and sent over a dedicated channel with format-version framing and packet splitting. Payloads over a soft threshold will warn and payloads that would exceed the transport's hard limit are skipped with an error and should never result in desyncs. The system will try its hardest to not exceed the thresholds.Client Assets
On the client, Strata becomes the actual storage and lookup backend behind the vanilla resource system, with a hard-capped patch surface:
SimpleReloadableResourceManagerreloadResourcesforwards the pack list into Strata, then runs the untouched listener notification (Forge's selective reload preserved). Same class, constructor, and signatures.FallbackResourceManagerLookups return genuine vanilla
SimpleResourceinstances (the pack name preserved from the originating pack for bug-compatibility), which defuses the common downcasts in the ecosystem. Known pack types (zip, folder, vanilla defaults) are unwrapped to their underlying files and memory-mapped, unknown third-partyIResourcePackimplementations are wrapped in a passthrough source with no existence caching (and this will be noted in logging and debugging). Strata still ultimately aims for correctness over speed. Stay compatible with dynamic packs. Mods that reflect into the old per-domain manager internals find the fields present but empty, this is the one documented behavioral break and a needed one.Compatibility and Risks
Dependencies
N/A
References
OreDictionarywhich is the limitation Strata's first consumer replaces and the main motivation to kickstart this proposal.Guidelines
All reactions