diff --git a/README.md b/README.md index 7b59153..388da49 100644 --- a/README.md +++ b/README.md @@ -1,292 +1,110 @@ # Rivencraft -Rivencraft is a clean, incremental port of the ideas and gameplay of -[Mystcraft Legacy](https://github.com/Mystcraft/Mystcraft-Legacy) to Minecraft -1.21.1 on NeoForge. +Write a book. Name an Age. Step into a world that did not exist before. -The bootstrap, books/linking, Ages, writing-system, and Age-grammar milestones are complete. -The writing system uses a data-pack symbol catalog and persistent -ordered-writing model. Linking Books and Descriptive Books create persistent -Ages from either authored supported symbols or seeded random completion. -Milestone 6 instability work is in progress: Ages persist frozen instability -scores and provider plans, and the bounded runtime implements status effects, -colored decay, lightning, explosions, meteors, burning, and erosion. The -[Milestone 5 acceptance record](docs/MILESTONE_5_ACCEPTANCE.md) lists the -supported grammar, optional integrations, and limits; the -[instability runtime policy](docs/INSTABILITY_RUNTIME.md) records the current -effects and modern safety boundaries. +Rivencraft brings the books, writing stations, symbols, and unpredictable Ages +of [Mystcraft Legacy](https://github.com/Mystcraft/Mystcraft-Legacy) to +Minecraft 1.21.1 on NeoForge. Mystcraft veterans will recognize the writing +flow, D'ni-inspired glyphs, libraries, crystals, and instability. Rivencraft +also lets you author modern terrain, biomes, structures, seasons, and celestial +bodies. It is an active pre-public-release project, not a byte-for-byte copy of +the 1.12.2 mod. -Milestone 4 now includes stackable blank Pages plus server-created symbol, -unresolved-symbol, and baseline link-panel states. Page visuals follow the -legacy runtime-composition rules. Craft an Ink Vial from two black dye, a glass -bottle, and a water bucket; the bucket is returned. Hold vanilla paper in one -hand and use the Ink Vial in the other to prepare a baseline Link Panel Page. -Each vial holds 16 ink units, and the default link-panel cost is two units. -The spent vial returns a glass bottle. Server owners can adjust the symbol-copy -and link-panel costs in the `writing` section of `rivencraft-server.toml`. -The Ink Mixer now offers a Legacy-style station for producing Link Panels: -insert paper and Ink Vials, then take a Link Panel from the result slot. Each -vial fills its 16-unit basin and returns a glass bottle; taking one Link Panel -uses the full vial. No server tick is needed. The earlier paper-in-one-hand -method still uses the configurable link-panel ink cost. Ink Mixer -additives can roll Disarm, Maintain Momentum, and Intra Linking onto a Link -Panel. Descriptive Books retain Disarm and Momentum; Intra Linking is only -meaningful for same-dimension travel with a Linking Book. -TerraBlender 1.21.1 integration is optional: registered modded biomes receive -Biome pages, with cave and Nether biomes in a separate Cave Biome creative tab. -TerraBlender Overworld/Nether regions extend compatible Age -biome pools. See [the compatibility matrix](docs/TERRABLENDER_COMPATIBILITY.md) -for supported versions and in-game verification. -Serene Seasons 1.21.1 can maintain separate season progress in an Age dimension. -Rivencraft automatically whitelists loaded Ages without a restart when Serene is installed; -season pages can instead opt an Age out or hold it in one season. -See [the optional compatibility guide](docs/SERENE_SEASONS_COMPATIBILITY.md) -for the supported manual configuration and current limits; Rivencraft does not -require Serene Seasons or GlitchCore. -The Writing Desk can copy a selected written page from its collection tabs. -The Writing Desk is now craftable from planks, a glass bottle, and a feather. -It occupies two adjacent blocks, with one shared inventory; loaded paper and -the selected page appear on its Legacy-inspired model. -Desks placed before 0.3.6.1 keep their old one-block footprint until broken -and placed again; leave an adjacent space when replacing one. -Its 88 collection tabs, paper input, ink vial input, empty glass-bottle return, -16-unit ink meter, and inventory persist -through world saves; breaking it drops stored contents. The desk screen is a -functional foundation using Legacy's original desk and scrollbar textures. -Four tabs are visible at once; the controls above and below them move by four -(1–4, 5–8, through 85–88). Each viewer can browse a different group. Existing -four-tab desks retain their collections and supplies when upgraded. -With a notebook selected, pick up a Page from your inventory and click an empty -position on the left surface to store one page there. Clear search/filter/sort -and turn off `ALL` to place pages at specific positions; occupied positions are -never overwritten. A notebook tab exposes all 27 notebook positions (scroll to -reach the rest); a loose-page tab exposes only its one occupied position. -Empty tabs have no page storage until a notebook or page is put in the tab slot. -Click a page on the desk surface to select it for copying. Drag a page from one -notebook position to an empty position to move it, or onto another page to swap -their positions. This also works between notebooks shown in the `ALL` view. -Drag a page out of the panel to turn it back into an item. Drop it over a player -inventory slot or desk tab to place it there; a tab with a notebook adds it to -the first open position. Dropping elsewhere leaves the page on the cursor for -normal inventory placement. The source notebook or tab loses the page only after -a valid drop. -Put vanilla paper in the -upper-left slot and Ink Vials in the upper-right; a copy appears in the -lower-left result slot when the selected page is valid. Taking it consumes one -paper and, by default, one ink. An available vial refills an empty ink meter -when inserted or when copying uses its last ink, and returns a vanilla glass -bottle to the lower-right slot. Removing a bottle also retries a blocked refill. -Refilling waits if that slot is full or contains another item. The `AZ` control sorts -visible pages by name; `ALL` shows pages held across every desk tab. Search and -category filtering narrow the view without granting new pages, and cross-tab -copy selection is checked by the server. -Selecting a stored page on the desk surface shows its Legacy-proportioned -portrait preview and name above the player inventory. +## Getting started -## Book Binder +1. Make an Ink Vial from two black dyes, a glass bottle, and a water bucket. + Craft an Ink Mixer, Writing Desk, and Book Binder. +2. Put paper and an Ink Vial in the Ink Mixer and take a Link Panel Page. Add + ingredients to the basin first if you want a chance of link modifiers. +3. Put leather or an empty Notebook and the Link Panel in the Book Binder. + Name the Age and take the Descriptive Book. A book with only a Link Panel + creates a seeded random Age; add Symbol Pages to author its features. +4. Open the book to create its Age, then click the destination preview to + travel. Bring a bound Linking Book for the return trip: automatic Return + Links are off by default. -For a new survival Age: craft an Ink Vial, an Ink Mixer, a Writing Desk, and a Book Binder. -Put vanilla paper and an Ink Vial in the Ink Mixer to make a Link Panel -Page. Place leather (or an empty Notebook) and the Link Panel in the Book Binder, -enter an Age name, and take the finished Descriptive Book. Written symbol Pages -are optional; without them, the book creates a random Age. To duplicate a Page -you already own, store it in a Writing Desk tab, select it, supply paper and ink, -and take the copied Page from the output. A Notebook can hold and transfer a -larger ordered collection of Pages between desks and the binder. +Find new Symbol Pages in Age libraries, or copy pages you already own with +paper and ink at the Writing Desk. The Notebook stores ordered pages, including +intentional gaps. Written pages can control terrain, biome distribution, +weather, lighting, seasons, sky color, suns and moons, decorations, structures, +and more. Incorrect or excessive writing can make an Age unstable; instability +may bring decay, lightning, explosions, meteors, burning, erosion, and other +hazards. Some combinations are still unsupported and the Binder reports them +before consuming ingredients. -With JEI installed, Rivencraft displays the Ink Mixer operation in its own -category and the hand-use, copying, binding, and Linking Book crafting flows in a Writing category. -The Link Panel and individual symbol Pages, including registered vanilla biome -pages, appear as separately searchable JEI ingredients. -Creative inventory separates Link Pages and each symbol category into their own -tabs, including distinct Biome and Biome Generation tabs. Blank Pages are hidden -from creative inventory and JEI. -JEI is optional; vanilla crafting -recipes and all writing interactions work without it. For a development client -with JEI, run `./gradlew runClient -PwithJei=true`. +Place a Rivencraft book or page on a vanilla Lectern to convert it to a Book +Stand. Crystal formations yield shards for Crystal Blocks. A connected crystal +frame with a Book Receptacle and a bound book forms a portal; frames can stand +upright or lie flat. Libraries, Book Stands, and portals use Legacy-inspired +assets and sounds. Handheld travel drops the used book at the departure point, +so plan how you will retrieve it. -Four Crystal Shards craft one Crystal Block; eight Crystal Blocks around an -empty crafting-grid center make one Book Receptacle. Picking up a shard unlocks -the block recipe, and picking up a block unlocks the Receptacle recipe. The -existing Ink Vial recipe uses two black dyes, a glass bottle, and a water -bucket; the empty bucket stays in the crafting grid. These JSON recipes appear -in JEI when it is installed and can be replaced by a data pack at -`data/rivencraft/recipe/.json`. To test a replacement, use a fresh -world or `/reload`, then check the result in a crafting table and vanilla -Crafter. A vanilla Lectern converts to a Rivencraft Book Stand when a Rivencraft -book or page is placed on it; this in-world interaction has no JEI guide. -With Create installed, Crystal Blocks and Book Receptacles have an optional Ponder scene -showing crystal-frame construction, book activation, and a parrot crossing the portal. Use the Ponder shortcut -while hovering either item; Rivencraft does not require Create to run. -Crystal frames may stand vertically or lie flat: a 3×3 Crystal Block ring with -an empty center forms a horizontal portal when a Book Receptacle is attached to -the top or underside of one of its blocks. Insert a bound Linking or Descriptive -Book to activate it; the book remains visible on the receptacle. A Linking Book aimed at its -own dimension still opens a visual portal, but travel requires Intra Linking. +## Requirements and optional integrations -Sky writing has shared Red, Green, Blue, White, and Black pages plus 25 D'ni -number pages (0–24). Put colors before a Sky, Sun, or Moon page to color that -target; trailing colors tint the sky. A number immediately before a color sets -its strength to number × 10. Without a number, a color gets a seeded random -strength from 0–24. RGB contributions add in any order, White brightens all -channels, Black subtracts from all channels, and omitted channels remain zero. -The resulting sky color also tints book previews and fallback portal surfaces. -An Age may have no suns or moons at all; Static Sky animates with fine-grained -television-like noise. Sun and Moon pages add -independent bodies; preceding color, size, angle, period, phase, and direction -pages configure the next body. The third and each subsequent sun add increasing -instability. Overlapping moon and sun discs produce an eclipse: the moon darkens, -the sun's color brightens at close alignment, and world and sky lighting dim. -Newly generated page-free Ages also choose a deterministic sky, -color, and celestial arrangement from their Age seed; older saved Ages retain -their previous sky. +Install the same gameplay mods on the client and server: -Craft a Book Binder from three iron ingots and five planks. Put leather or an -empty Notebook in the left cover slot. Enter a title, then place a Link Panel -Page first in the horizontal page strip. Click a page in the strip with a -written Page on the cursor to insert it before that position; click it with an -empty cursor to take it out. The arrows or mouse wheel scroll through up to -27 written Pages. Clicking the strip with a filled Notebook imports its Pages -at that position and leaves the emptied Notebook on the cursor. The right slot -outputs a Descriptive Book once the title, cover, and Link Panel are present. -Inputs are consumed only when the result is taken. +- Minecraft **1.21.1**, NeoForge **21.1.264 or newer in the 21.1 line**, and + Java 21. +- **Dynamic Dimensions AR Edition 0.9.1** and its **Bad Packets 0.8.2** + dependency. These are required for Age dimensions. -The completed book receives its Age identity when assembled. With only a Link -Panel, it describes a randomly generated Age. Authored writing is compiled once -into a versioned, server-owned Age plan. The plan freezes the Age seed, resolved -symbol meanings, page order, and runtime profile, so later data-pack changes do -not reinterpret an existing Age. An authored terrain page selects its runtime -profile. Built-in pages cover Standard, Amplified, Cave, Island, Flat, and Void; -authored biome, environment, and supported feature pages also affect new Ages. -The -writing is also stored on the book; the Descriptive Book screen's turned pages -show ordered symbols and unresolved IDs. The Descriptive Book screen follows -Legacy's book spread: the title and Age details are on the left, the destination -preview is on the right, and clicks in the bottom corners turn pages. Ordered -symbol pages appear before the final blank spread with their runtime-rendered -pictograms. Link-panel-only books use Rivencraft glyphs for their generated -Age description. +Rivencraft works without any of these optional integrations: -## Linking Book +| Mod | What it adds | +| --- | --- | +| JEI | Searchable Pages and guides for the Ink Mixer, Writing Desk, Book Binder, and Linking Book crafting. | +| Create (bundles Ponder) | Ponder book snapshots and a crystal-portal construction guide. | +| Immersive Portals | Live destination views in book previews and crystal portals. In `AUTO` preview mode it takes priority over Ponder. | +| TerraBlender | Eligible biomes from mods using its Overworld/Nether regions become Age biome candidates and Pages. Biomes O' Plenty is one example; it is not hard-coded or required. | +| Serene Seasons (with GlitchCore) | Independent per-Age seasons, including normal seasons, no seasons, and four eternal-season modes. | +| Ruins | Authorable Ruins-template Pages and Ruins structures in Ages. | -Craft an unwritten Linking Book from one Link Panel Page and one leather in any -two crafting-grid positions. An unbound book opens a screen where it can be named and bound to the -current location. A bound book opens its destination screen and travels when -its preview panel is clicked. The destination is stored on the item and -survives inventory and world saves. Travel only succeeds if the target -dimension is loaded and a non-fluid, passable landing position can be found -nearby. -The Linking Book uses the same Legacy page layout and bottom-corner page turns -as the Descriptive Book, but without the Descriptive Book's gold cover edges. -Both screens play Minecraft's book-page-turn sound when a page is turned. +The optional builds used in the latest development checks are JEI +19.69.0.457, Create 6.0.10, Immersive Portals 6.0.7, TerraBlender 4.1.0.8, +Serene Seasons 10.1.0.9, and Ruins 1.21.1.2NF. See the +[compatibility review](docs/MILESTONE_7_COMPATIBILITY_REVIEW.md) for the +evidence and limits of those combinations. -Successful book travel plays Mystcraft Legacy's original linking sound at both -the departure and arrival locations. +Without Create or Immersive Portals, book previews use Rivencraft's built-in +Legacy-style animation. Ecliptic Seasons is **not supported**: its 1.21.1 API +does not offer the live dimension/season control Rivencraft needs. -Right-click an empty vanilla lectern with a Rivencraft book to convert it into -a Book Stand and place the book on it. Use a Linking Book on the stand by -right-clicking with an empty hand. Sneak-right-click with an empty hand to take -the book back and restore the vanilla lectern. Breaking a Book Stand drops both -the stored book and a vanilla lectern. +## Saves, settings, and current limits -Bound Linking Books show a rotating Ponder snapshot when Create is installed. -Without Create, they use a built-in animated linking panel tinted by the -destination sky. The server independently validates every link. +Ages and bound books use persistent UUIDs. Back up the **entire world folder** +before changing mod versions, data packs, or optional biome mods: the Age +registry and dimension data must be restored together. Rivencraft preserves +known older save formats where possible, but pre-public-release worlds are not +promised universal compatibility. Missing custom symbols retain their IDs in +books; missing registry entries or dimension providers can still prevent an +Age from loading. See [current implementation](docs/CURRENT_IMPLEMENTATION.md) +for supported behavior and deliberate limits. -## Descriptive Book +Server settings include `descriptive_books.return_link_mode` (default +`DISABLED`), `libraries.loot_mode` (`LEGACY` or `AGE_ONLY`), writing ink costs, +season enrollment, and instability controls. The client setting +`book_preview.mode` chooses `AUTO`, `IMMERSIVE_PORTALS`, `PONDER`, or `LEGACY`. +An operator can inspect Ages and instability with `/rivencraft age` and find +libraries with `/rivencraft library nearest`. The creative-only Link Modifier +can edit link properties and retire an Age; use it carefully and keep backups. -Open a page-free Descriptive Book, name its Age, and press Create. Rivencraft -assigns the book a permanent Age UUID, selects a deterministic base terrain, -creates a dedicated runtime dimension, and prepares a five-by-five cobblestone -arrival platform at the -highest suitable nearby terrain. Solid blocks and tree leaves are valid -footing. Candidate columns are scanned from the top down so cave floors are not -selected, and fluid-covered columns are excluded. If no suitable surface is -available, the platform is generated at Y=63. Reopen the book and click its preview to travel. -The book cannot reopen while its Age is being generated, whether held or on a -Book Stand. Once creation finishes, reopening shows the written book. -Copies of the written book continue to resolve to the same persistent Age -across server restarts. +## Project and development -New Age dimension folders use a Minecraft-safe lowercase form of the assigned -name. Conflicts receive incrementing suffixes such as `_2` and `_3`; existing -Age folders are never renamed. +This repository is the development source. See the [current implementation](docs/CURRENT_IMPLEMENTATION.md), +[milestone summaries](docs/MILESTONES.md), [technical document index](docs/README.md), +and [issues](https://truenas-scale.tail180fb1.ts.net:8443/dnorberg/rivencraft/issues). +Milestone 7 is in progress; its [regression matrix](docs/MILESTONE_7_REGRESSION.md) +distinguishes automated checks from manual checks and known limitations. -Page-free Ages deterministically select one of six base terrains inspired by -Mystcraft Legacy: Standard, Amplified, Island, Cave, Flat, or Void. The chosen -profile is saved with the Age and does not change when the server restarts. - -Linking creates a bound Return Link to the exact source position and orientation -according to the configured Return Link policy. - -Server owners can set `descriptive_books.return_link_mode` in Rivencraft's -server config to `DISABLED` (default), `ON_CREATION`, or `ALWAYS`. -Age creation, persistence, lifecycle commands, configuration, backups, and -administrator recovery are documented in [docs/AGES.md](docs/AGES.md). -Data-pack runtime profiles are documented in -[docs/AGE_PROFILES.md](docs/AGE_PROFILES.md). - -## Requirements - -- A Java 21 JDK -- Minecraft 1.21.1 -- NeoForge 21.1.264 or newer in the 21.1 line -- Dynamic Dimensions AR Edition 0.9.1 and Bad Packets 0.8.2 (client and server) - -Ruins is optional. When installed, its configured structures have individual -authorable pages; see [Ruins compatibility](docs/RUINS_COMPATIBILITY.md). - -Create is optional. When installed, Rivencraft uses its Ponder renderer for -book previews when Immersive Portals is absent. Immersive Portals is also -optional and takes priority in the default `AUTO` mode, providing a live -rotating destination view. Its Explore button enables WASD and jump/sneak -camera movement plus left-drag camera look; right-click returns to the rotating -view. Without either integration, Rivencraft uses its built-in animated linking -panel. Clients can set `book_preview.mode` in -`rivencraft-client.toml` to `AUTO`, `IMMERSIVE_PORTALS`, `PONDER`, or `LEGACY`. -Unavailable optional renderers safely fall back to `LEGACY`. -The complete backend matrix, supported combinations, cleanup behavior, and -known upstream limits are documented in -[docs/PREVIEW_COMPATIBILITY.md](docs/PREVIEW_COMPATIBILITY.md). - -## Build and run - -```shell +```sh ./gradlew build -./gradlew runClient +./gradlew runGameTestServer ``` -The built mod is written to `build/libs/`. -The default dedicated-server GameTest run is deliberately small. Use -`./gradlew runGameTestServer -PwithArchivedGameTests=true` for the complete -regression suite before a release or when investigating a related issue. See -[docs/GAMETESTS.md](docs/GAMETESTS.md). +The JAR is written to `build/libs/`. Development runs need the required mods; +see [GameTest guidance](docs/GAMETESTS.md) for focused archived suites. +Versions use `x.y.z.b`: major release, completed milestone, release slice, +then fixes/additional work within that slice. -## Versioning - -Rivencraft versions use `x.y.z.b`: - -- `x` changes for a major public release. -- `y` changes when a development milestone is completed. -- `z` changes for each release slice within the current milestone. -- `b` changes for bug fixes and additional work within that release slice. - -The build component resets to zero when a new release slice begins. Completing -a milestone increments `y` and resets both `z` and `b` to zero. For example, -milestone 2 development can progress from `0.1.1.0` to `0.1.1.1`, then -`0.1.2.0`; completing the milestone produces `0.2.0.0`. - -## Porting approach - -Mystcraft Legacy targets Forge for Minecraft 1.12.2. Rivencraft therefore -ports one subsystem at a time onto current registries, codecs, data packs, and -NeoForge APIs instead of importing the legacy source tree wholesale. - -See [docs/PORTING.md](docs/PORTING.md) for the roadmap, live Gitea tracking -links, architectural direction, and provenance rules. - -## License - -Rivencraft is licensed under LGPL-3.0-or-later. Mystcraft Legacy is also -LGPL-3.0; copied or adapted upstream code must retain its copyright and license -notices. See [LICENSE](LICENSE) and [NOTICE](NOTICE). +Rivencraft is licensed under LGPL-3.0-or-later. Legacy-derived material is +credited in [NOTICE](NOTICE) and [asset provenance](docs/ASSET_PROVENANCE.md). diff --git a/docs/AGES.md b/docs/AGES.md index ed0f63e..b70619b 100644 --- a/docs/AGES.md +++ b/docs/AGES.md @@ -11,7 +11,7 @@ suffixes such as `_2` and `_3`. The selected terrain profile, biome distribution, lighting, weather, celestial period, celestial angle, and phase are deterministic and become part of the -saved definition. A five-by-five cobblestone platform with clear headroom is +saved definition. A five-by-five non-dropping Crystal Platform with clear headroom is prepared at a safe arrival point. The completed book and every copy carrying the same UUID resolve to that definition. @@ -42,10 +42,11 @@ on demand when an administrator previously unloaded it. Unloading retains the definition and world folder, so existing books continue to reference the same Age and later reload the same terrain. -Rivencraft does not automatically delete Ages. Safe permanent deletion needs a -complete reference index for books in inventories, containers, Book Stands, -and unloaded chunks. Until that exists, retaining the definition prevents an -administrator action from silently orphaning books. +Rivencraft does not automatically delete Ages. The operator-only creative Link +Modifier can deliberately retire an unoccupied Age: it unloads the dimension, +moves its data to a sibling `-retired-ages/` archive, and records +the UUID as dead so existing books cannot silently regenerate it. Back up the +whole world first. Destination-preview chunk loaders are temporary. Rivencraft releases them when the book closes, the player travels, the player disconnects, the destination @@ -101,8 +102,8 @@ Use this sequence when a book cannot load an existing Age: matching dimension data and authoritative world saved data from the same backup generation. Do not combine them from different backups. -There is intentionally no delete command in Milestone 3. Do not manually -delete an Age definition or rename its dimension folder: books may exist in -inventories, containers, Book Stands, or unloaded chunks, and would be -orphaned. `/rivencraft age unload` is the supported reversible maintenance -operation. +Do not manually delete an Age definition or rename its dimension folder: +books may exist in inventories, containers, Book Stands, or unloaded chunks. +`/rivencraft age unload` remains the reversible maintenance operation. The +Link Modifier's retirement control is not reversible through a book UI; +recover its archived data and registry together from a full-world backup. diff --git a/docs/CURRENT_IMPLEMENTATION.md b/docs/CURRENT_IMPLEMENTATION.md new file mode 100644 index 0000000..eceb970 --- /dev/null +++ b/docs/CURRENT_IMPLEMENTATION.md @@ -0,0 +1,96 @@ +# Current implementation (Milestone 7 development) + +This is the maintained technical overview of Rivencraft as of the 0.6.11.0 +slice. It describes implemented behavior, not every behavior in Mystcraft +Legacy. [The README](../README.md) is the player introduction; +[milestone summaries](MILESTONES.md) explain how the implementation arrived +here. The linked deep dives are retained as historical audits and should be +read with their version dates in mind. + +## Books, linking, and previews + +- Linking Books bind a named position and direction. Same-dimension use needs + Intra Linking. Descriptive Books refer to a persistent Age UUID and keep + their authored page order. Copies keep that identity. +- Book screens use Legacy-inspired page spreads and destination panels. `AUTO` + selects optional Immersive Portals first, then Create/Ponder, then the built-in + animation. Travel is validated on the server regardless of renderer. +- Handheld travel drops the active book at its departure point as a visible + book entity. Book Stands and crystal portals keep the book in place. Disarm + drops carried items; Maintain Momentum rotates and carries velocity. +- A vanilla Lectern converts to a Book Stand when given a Rivencraft book or + page. A Book Receptacle in a connected Crystal Block frame activates a + vertical or horizontal portal. Immersive Portals can display a live view; + otherwise the Legacy-style animated surface remains. Portal arrivals use + existing safe footing; a Descriptive portal does not generate a platform. +- Automatic Return Links default to `DISABLED`, with `ON_CREATION` and `ALWAYS` + server options. Linking Books do not generate platforms by default; a + creative Link Modifier can explicitly enable that property. + +See [preview modes](PREVIEW_COMPATIBILITY.md), [Legacy M7 audit](LEGACY_M7_AUDIT.md), +and [Age administration](AGES.md). + +## Ages, writing, and structures + +- Dynamic Dimensions AR Edition creates Age worlds. Each Age has a saved UUID, + safe dimension ID, seed, spawn, traits, versioned generation plan, and + optional season mode. Page-free or Link-Panel-only books get seeded random + choices. Authored supported pages override the appropriate choices. +- Terrain choices are Standard, Amplified, Island, Cave, Flat, and Void. Cave + terrain uses cave/Nether biomes and an appropriate spawn search; normal + terrain starts from exposed surfaces. If no safe arrival site exists, the + Age-creation path can construct a non-dropping Crystal Platform at Y=63. +- The server catalog accepts typed data-pack symbols. The Binder preserves + written order and diagnoses conflicts. Symbol Pages are composed at runtime + from Legacy glyph assets; number pages 0–24 set color strength and, when + immediately before a Length, Angle, or Phase modifier, their numeric value. + Celestial pages can create multiple suns/moons with independent color, size, + angle, period, phase, and direction; authored eclipses affect light. +- The Writing Desk stores tabs and copies known Symbol Pages with paper and + ink. Notebooks hold ordered pages; the Binder consumes a Link Panel and cover + to make a finished Descriptive Book. The Ink Mixer makes Link Panels and + rolls Disarm, Intra Linking, and Maintain Momentum from additives. Blank + Pages are internal bases, not a craftable or JEI-listed output. +- Features include terrain additions, decorations, configurable block/fluid + materials, ores, vanilla structures including newer 1.21 structures, + Legacy-style libraries, crystals, obelisks, and star fissures. Optional Ruins + templates gain authorable pages. Some biome/terrain combinations or feature + placements are deliberately unsupported and are diagnosed during binding. + +See [symbol semantics](SYMBOL_SEMANTICS.md), [writing data](WRITING_DATA.md), +[generation profiles](AGE_PROFILES.md), and [library/Ruins rules](RUINS_COMPATIBILITY.md). + +## Instability and optional mods + +Invalid or excessive writing and valuable generated blocks can add +instability. Block abundance is profiled in loaded chunks, so its contribution +and selected effects can change as the Age is explored; the authored plan is +not rewritten. Effects are bounded to loaded Ages and include status effects, +colored decay, lightning, explosions, meteors, burning, and erosion. Operator +commands expose the score and providers; the book GUI does not. See +[scoring](INSTABILITY_SCORE.md) and [runtime policy](INSTABILITY_RUNTIME.md). + +JEI, Create/Ponder, Immersive Portals, TerraBlender, Serene Seasons with +GlitchCore, and Ruins are optional. TerraBlender can supply biomes from any mod +using its eligible regions; Biomes O' Plenty is a test example. Serene Seasons +has per-Age normal/no/eternal modes and live Age enrollment. Ecliptic Seasons +is not integrated because its 1.21.1 API lacks supported live registration and +per-Age calendar control. See the [optional-mod matrix](MILESTONE_7_REGRESSION.md). + +## Persistence and boundaries + +An Age registry definition and its dimension folder belong to the **same +whole-world backup**. Known older book/writing, plan, workstation, and Age +formats have migration or stable-default handling. Unknown symbol IDs remain +in books; missing registry objects or removed dimension mods can still prevent +world loading. The OP-only Link Modifier can retire an unoccupied Age. That +operation marks its UUID dead and moves its dimension data to a sibling +`-retired-ages/` folder; it is not ordinary book maintenance. +Do not use it without a backup. + +This is not an exact port of every Legacy generator, page, hazard, or client +renderer. Modern safety budgets and NeoForge's dimension/biome APIs require +adaptations. The [Milestone 7 regression matrix](MILESTONE_7_REGRESSION.md) +separates tested behavior from unverified multiplayer/restart/optional-mod +combinations. Issue [#84](https://truenas-scale.tail180fb1.ts.net:8443/dnorberg/rivencraft/issues/84) +is the final Milestone 7 acceptance and release gate. diff --git a/docs/GAMETESTS.md b/docs/GAMETESTS.md index d316cd5..325dbe2 100644 --- a/docs/GAMETESTS.md +++ b/docs/GAMETESTS.md @@ -1,5 +1,9 @@ # GameTest suites +The current Milestone 7 results and manual handoffs are in the +[regression matrix](MILESTONE_7_REGRESSION.md). This file documents the test +mechanics and older scenario-specific handoffs. + The default dedicated-server run is a small smoke suite. Its Rivencraft tests cover core linking, deterministic Age definition, writing persistence, legacy data migration, workstation transactions, and forged/replayed menu actions. diff --git a/docs/MILESTONES.md b/docs/MILESTONES.md new file mode 100644 index 0000000..b2f22c9 --- /dev/null +++ b/docs/MILESTONES.md @@ -0,0 +1,65 @@ +# Milestones at a glance + +This is the condensed milestone history. The [current implementation](CURRENT_IMPLEMENTATION.md) +is authoritative for what the latest development build does; older acceptance +and audit files remain available as dated evidence, not competing current +instructions. [Gitea milestones](https://truenas-scale.tail180fb1.ts.net:8443/dnorberg/rivencraft/milestones) +hold the live issue status. + +## 1 — Bootstrap (complete) + +Established the NeoForge 1.21.1/Java 21 project, mod identity, registries, +assets, build, and first automated checks. The port deliberately shares +Mystcraft's concepts while maintaining its own modern package and data model. +See [porting principles](PORTING.md) and [asset provenance](ASSET_PROVENANCE.md). + +## 2 — Books and linking (complete) + +Introduced persistent Linking and Descriptive Books, conversion of vanilla +Lecterns into Book Stands, named books and Ages, safe travel, destination +previews, and a configurable Return Link policy. Later milestones refined +renderer selection and portal travel. See [preview compatibility](PREVIEW_COMPATIBILITY.md). + +## 3 — Ages (complete) + +Added persistent runtime dimensions, deterministic random Age traits, +name-derived unique dimension folders, terrain/biome choices, spawn recovery, +and Age lifecycle commands. See [Age administration](AGES.md), +[runtime profiles](AGE_PROFILES.md), and [random weights](RANDOM_AGE_WEIGHTS.md). + +## 4 — Writing system (complete) + +Introduced Symbol Pages, Link Panels, Notebooks, the Writing Desk, Book Binder, +and Ink Mixer. Writing order and unknown symbol references persist instead of +being inferred from UI text. See [writing data](WRITING_DATA.md), +[descriptive-book presentation](DESCRIPTIVE_BOOK_PRESENTATION.md), and +[Ink Mixer audit](INK_MIXER_MODIFIER_AUDIT.md). + +## 5 — Age grammar (complete for the supported subset) + +Compiled supported pages into frozen Age plans: terrain, biome distribution, +environment, features, block/fluid materials, structures, crystals, and +fissures. Added optional TerraBlender and Serene Seasons adapters. This was +not a claim of complete Legacy grammar parity. See [M5 acceptance](MILESTONE_5_ACCEPTANCE.md), +[Legacy grammar audit](LEGACY_AGE_GRAMMAR_AUDIT.md), +[symbol schema](SYMBOL_SEMANTICS.md), [TerraBlender](TERRABLENDER_COMPATIBILITY.md), +and [Serene Seasons](SERENE_SEASONS_COMPATIBILITY.md). + +## 6 — Instability (complete) + +Added authored and live block-abundance scoring, frozen provider plans, +operator diagnostics, bounded status effects, decay, lightning, explosions, +meteors, burning, and erosion. Safety budgets are deliberate modern +adaptations. See [M6 acceptance](MILESTONE_6_ACCEPTANCE.md), +[Legacy instability audit](LEGACY_INSTABILITY_AUDIT.md), and +[runtime policy](INSTABILITY_RUNTIME.md). + +## 7 — Structures and polish (in progress) + +Added Legacy-layout Age libraries, page-bearing stands, crystal portals and +receptacles, the handheld dropped-book entity, data-driven recipes, +advancements, asset/audio polish, optional Ruins, compatibility updates, and +expanded sky/glyph pages. Issue #83 covers regression, migration, and document +consolidation; issue #84 is the final milestone acceptance gate. See the +[M7 Legacy audit](LEGACY_M7_AUDIT.md), [M7 regression matrix](MILESTONE_7_REGRESSION.md), +and [M7 compatibility review](MILESTONE_7_COMPATIBILITY_REVIEW.md). diff --git a/docs/MILESTONE_7_REGRESSION.md b/docs/MILESTONE_7_REGRESSION.md new file mode 100644 index 0000000..94c5bf0 --- /dev/null +++ b/docs/MILESTONE_7_REGRESSION.md @@ -0,0 +1,71 @@ +# Milestone 7 regression and migration matrix (issue #83) + +This is a **working** acceptance record for the 0.6.11.0 candidate, not the +Milestone 7 completion sign-off. Issue #84 owns the final gate. A passing +GameTest is server-side evidence; it is not proof of client rendering, +multiplayer timing, or every optional-mod combination. + +## Automated evidence + +| Area | Focused check | Candidate result | +| --- | --- | --- | +| Dedicated server, core linking, book/Age identity, old writing and workstation formats | `./gradlew --offline runGameTestServer`; includes `CoreGameTests`, `AgePersistenceGameTests`, `CrystalPortalGameTests`, M7 crafting/advancement checks | 91/91 required GameTests passed on NeoForge 21.1.264; clean shutdown and fresh-world removal. | +| Deterministic structures and terrain | `./gradlew --offline runGameTestServer -ParchivedGameTestSuites=generation`; includes library template/spacing/loot/stand checks, cave profiles, vanilla structures, crystal formations, fissures | 129/129 passed after correcting one village test to assert its own contribution rather than a seed-dependent total; fresh-world removal. | +| Sky, glyph numbers, seasons, creative/JEI page feed | `./gradlew --offline runGameTestServer -ParchivedGameTestSuites=environment` | 103/103 passed on the current candidate, including issue #94's number-page check; fresh-world removal. | +| Packaged models and data | `Milestone7ResourceGameTests.coreModelsResolveBundledTextures` in the core suite; parses critical block models, checks their Rivencraft texture references and the library NBT resource | Passed in the 91-test core run. This checks resource presence, not visual orientation or transparency. | +| Recipe behavior | `Milestone7CraftingGameTests` and `LinkingBookRecipeGameTests` through the loaded Recipe Manager | Passed in the core run; confirms crystal and receptacle quantities, ink bucket return, no Blank Page crafting, and Linking Book ingredients. | +| Portal state/save-load | `CrystalPortalGameTests` plus `AgePersistenceGameTests` in the core suite | Passed; covers activation/release, fallback orientation, horizontal/underside placement, uninterrupted surface, cooldown, retired Age and seed/plan identity. Full server restart remains manual. | + +The generation archive initially failed +`AgeStructureGameTests.mixedEndAndTaigaVillagesChargeForEndPortion` because it +compared the total instability score (1200) with the village-only expectation +(200). A seed-completed sky legitimately supplied an extra sun (+1000). The +production scoring was unchanged; the check now isolates the +`forced_biome_villages` contribution. The rerun passed 129/129. This is a +repaired assertion, not a hidden feature failure. + +Every default GameTest run creates a new world with Minecraft's fixed seed 0 +and deletes it after pass or fail. See [GameTest guidance](GAMETESTS.md). +Do not retry a storage-stalled GameTest: stop it, remove that automated +scenario, and give its exact steps to the player. No run in this issue has +stalled so far. The older full-archive storage stall is historical and is not +counted as a current pass. + +## Old-save boundary (#60) + +Issue #60 established a policy of preserving old saves where practical. Tests +cover bare-list `OrderedWriting`, page-free books, unknown symbol IDs, older +Age definitions with stable defaults, plan-v3/plan-v4 interpretation, and +workstation inventory migration. A bound book's UUID and its Age's registered +dimension remain stable through ordinary load. This does **not** imply every +pre-public-release world, removed biome mod, missing dimension provider, or +changed datapack can be opened. Back up the full world; the Age registry and +dimension folders are a unit. The creative Link Modifier can intentionally +retire an Age, archiving its data and marking the UUID dead. That is an explicit +administrative boundary, not a silent migration. + +## Manual matrix for the player + +Use a backup or disposable save. Record the exact mod list and jar version. +These checks are **pending for 0.6.11.0** unless a later note records an actual +pass. Historical passes on earlier slices should not be silently promoted to +this candidate. + +| Scenario | What to verify | +| --- | --- | +| Full restart | Create a random and an authored Age, a library with a looted chest/page stand, an active portal, and a bound book. Stop and restart the server; UUID, terrain, loot removal, stand contents, portal activation, and return travel must persist. | +| Two players | Cross the same portal at nearly the same time, return independently, and use separate book previews. Cooldowns, destination positions, dropped-book ownership/retrieval, and preview chunk loaders must not interfere. | +| Cross-dimension travel | Compare the same Descriptive Book by hand, stand, and portal. Check footing/platform rules, arrival coordinates and facing, Disarm, Maintain Momentum, Intra Linking, and Return Link mode. | +| Client visuals/audio | Compare book spreads, glyphs, D'ni numbers, page/desk/binder/mixer models, 16×16 crystal buds and translucent elements, portal frame/surface order, skies/eclipses, page turns, and Legacy link sounds. A server test cannot observe pixels or audio. | +| Optional renderers | With neither Create nor Immersive Portals, confirm Legacy fallback. With Create alone, confirm Ponder snapshots and portal guide. With Immersive Portals, confirm live view, Explore exit, sky/sound isolation, portal travel, and fallback while chunks load. With both, `AUTO` should choose Immersive Portals. | +| Optional world/recipe mods | Test JEI guides/search, TerraBlender with a biome-producing addon (for example Biomes O' Plenty), Serene's six season modes plus Overworld independence/restart, and Ruins template selection/spawn weighting. Repeat startup without optional mods. | +| Migration | Copy a backed-up older development world. Verify books and Notebooks retain their Age/page IDs, a desk and stand keep contents, old Ages load or provide a clear error, and removing a custom symbol retains its unresolved ID. Do not test by modifying the only copy. | + +Historical evidence: the player approved NeoForge 21.1.264 on singleplayer +and a dedicated server with Immersive Portals; JEI 19.69.0.457 guides, Serene +Seasons 10.1.0.9's full per-Age matrix, Ruins generation, and issue #94's +numbered celestials each received an in-game pass on their respective earlier +release slices. See [compatibility review](MILESTONE_7_COMPATIBILITY_REVIEW.md), +[JEI review](MILESTONE_7_JEI_REVIEW.md), [Serene review](MILESTONE_7_SERENE_REVIEW.md), +and [Ruins notes](RUINS_COMPATIBILITY.md). The current candidate's broad +multiplayer/restart/optional-mod matrix remains unverified until tested. diff --git a/docs/PORTING.md b/docs/PORTING.md index 6f12f0c..4998221 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -1,5 +1,9 @@ # Porting roadmap +For a concise current status, start with [current implementation](CURRENT_IMPLEMENTATION.md) +and [milestone summaries](MILESTONES.md). This file retains the original +roadmap and architectural rationale. + Rivencraft ports Mystcraft Legacy from Minecraft 1.12.2 to NeoForge 1.21.1. The world-generation, dimension, registry, networking, and rendering APIs have changed substantially, so the work is divided into independently testable @@ -62,8 +66,9 @@ acceptance issue and all required implementation issues are complete. **Complete.** See the [Legacy instability audit](LEGACY_INSTABILITY_AUDIT.md) and [Milestone 6 acceptance](MILESTONE_6_ACCEPTANCE.md). 7. **Structures and polish** — libraries, crystals, portals, recipes, models, - sounds, advancements, compatibility, and migration tests. Start with the - [Milestone 7 Legacy audit](LEGACY_M7_AUDIT.md). + sounds, advancements, compatibility, and migration tests. **In progress.** + See the [Milestone 7 regression matrix](MILESTONE_7_REGRESSION.md) and + [Legacy audit](LEGACY_M7_AUDIT.md). ## Architectural direction diff --git a/docs/PREVIEW_COMPATIBILITY.md b/docs/PREVIEW_COMPATIBILITY.md index 07ccdb0..b3900b1 100644 --- a/docs/PREVIEW_COMPATIBILITY.md +++ b/docs/PREVIEW_COMPATIBILITY.md @@ -66,8 +66,11 @@ unstable in a particular graphics or mod configuration. For preview-related releases: -1. Run `./gradlew runGameTestServer -PwithArchivedGameTests=true` to verify the complete selection matrix, - core Age creation, persistence, and travel without optional renderers. +1. Run `./gradlew runGameTestServer` for the core selection matrix, Age + creation, persistence, and travel without optional renderers. Add the + relevant archives from [GameTest guidance](GAMETESTS.md); if a scenario + stalls, stop it and hand that exact check to a real client rather than + treating the interrupted run as a pass. 2. Open held Linking and Descriptive Books once with each available backend. 3. Open both book types from a Book Stand and confirm Explore is disabled. 4. With Immersive Portals, enter and leave Explore, travel between dimensions, diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..1e99bc6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +# Documentation map + +Start with the player-facing [project README](../README.md). The two maintained +technical entry points are [current implementation](CURRENT_IMPLEMENTATION.md) +and [milestone summaries](MILESTONES.md). Use [GameTest guidance](GAMETESTS.md) +to run checks. The files below retain detailed design, provenance, and +version-specific test evidence; an older milestone record may describe a +former behavior or loader version. + +| Milestone | Detailed records | +| --- | --- | +| M1–M3 | [Porting roadmap](PORTING.md), [Age administration](AGES.md), [Age profiles](AGE_PROFILES.md), [preview compatibility](PREVIEW_COMPATIBILITY.md), [random Age weights](RANDOM_AGE_WEIGHTS.md) | +| M4 | [Writing data](WRITING_DATA.md), [book presentation](DESCRIPTIVE_BOOK_PRESENTATION.md), [Ink Mixer audit](INK_MIXER_MODIFIER_AUDIT.md) | +| M5 | [Acceptance](MILESTONE_5_ACCEPTANCE.md), [Legacy grammar audit](LEGACY_AGE_GRAMMAR_AUDIT.md), [writing grammar](WRITING_GRAMMAR.md), [symbol semantics](SYMBOL_SEMANTICS.md), [TerraBlender](TERRABLENDER_COMPATIBILITY.md), [Serene Seasons](SERENE_SEASONS_COMPATIBILITY.md) | +| M6 | [Acceptance](MILESTONE_6_ACCEPTANCE.md), [regression](MILESTONE_6_REGRESSION.md), [Legacy audit](LEGACY_INSTABILITY_AUDIT.md), [score](INSTABILITY_SCORE.md), [live blocks](LIVE_BLOCK_INSTABILITY.md), [runtime](INSTABILITY_RUNTIME.md), [diagnostics](INSTABILITY_DIAGNOSTICS.md), [status effects](INSTABILITY_STATUS_EFFECTS.md), [decay](INSTABILITY_DECAY.md), [environmental hazards](INSTABILITY_ENVIRONMENTAL.md), [cloud platform](AGE_CLOUD_PLATFORM.md) | +| M7 | [Regression and manual matrix](MILESTONE_7_REGRESSION.md), [Legacy audit](LEGACY_M7_AUDIT.md), [visual/audio](MILESTONE_7_VISUAL_AUDIT.md), [advancements](MILESTONE_7_ADVANCEMENTS.md), [JEI](MILESTONE_7_JEI_REVIEW.md), [Serene review](MILESTONE_7_SERENE_REVIEW.md), [compatibility](MILESTONE_7_COMPATIBILITY_REVIEW.md), [Ruins](RUINS_COMPATIBILITY.md) | + +[Asset provenance](ASSET_PROVENANCE.md) applies across milestones. Detailed +audits are intentionally retained for source attribution and migration +history; these three entry points replace the former practice of treating +every dated task note as a current installation guide. diff --git a/gradle.properties b/gradle.properties index 4283207..57c3d06 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,7 +36,7 @@ mod_name=Rivencraft # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0-or-later # The mod version. Rivencraft uses the x.y.z.b policy documented in README.md. -mod_version=0.6.10.0 +mod_version=0.6.11.0 immersive_portals_version=6.0.7 terrablender_version=4.1.0.8 serene_seasons_version_id=pHEgQQUE diff --git a/src/main/java/net/dnorberg/rivencraft/book/AgeStructureGameTests.java b/src/main/java/net/dnorberg/rivencraft/book/AgeStructureGameTests.java index 3615521..6a0ed27 100644 --- a/src/main/java/net/dnorberg/rivencraft/book/AgeStructureGameTests.java +++ b/src/main/java/net/dnorberg/rivencraft/book/AgeStructureGameTests.java @@ -178,8 +178,11 @@ public final class AgeStructureGameTests { var age = AgeRegistrySavedData.resolveOrCreate(helper.getLevel(), new AgeReference(java.util.UUID.randomUUID(), "Mixed " + firstBiome), writing); int expected = firstBiome.equals("the_end") ? 200 : 0; - helper.assertValueEqual(age.instability().score(), expected, - "Each Village page must charge 100 if any selected biome needs the override"); + helper.assertValueEqual(age.instability().breakdown().stream() + .filter(part -> part.reason().equals("forced_biome_villages")) + .mapToInt(AgeInstability.Contribution::points).sum(), expected, + "Each Village page must charge 100 if any selected biome needs the override; " + + "seed-completed celestial instability is independent"); helper.assertValueEqual(age.instability().breakdown().stream() .filter(part -> part.reason().equals("forced_biome_villages")).count(), firstBiome.equals("the_end") ? 2L : 0L, diff --git a/src/main/java/net/dnorberg/rivencraft/gametest/Milestone7ResourceGameTests.java b/src/main/java/net/dnorberg/rivencraft/gametest/Milestone7ResourceGameTests.java new file mode 100644 index 0000000..4fa5f27 --- /dev/null +++ b/src/main/java/net/dnorberg/rivencraft/gametest/Milestone7ResourceGameTests.java @@ -0,0 +1,52 @@ +package net.dnorberg.rivencraft.gametest; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import net.dnorberg.rivencraft.Rivencraft; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; + +/** Small server-side guard against shipping missing model textures or the library template. */ +@GameTestHolder(Rivencraft.MOD_ID) +@PrefixGameTestTemplate(false) +public final class Milestone7ResourceGameTests { + private Milestone7ResourceGameTests() {} + + @GameTest(template = "empty") + public static void coreModelsResolveBundledTextures(GameTestHelper helper) { + for (String model : List.of("book_binder", "ink_mixer", "writing_desk", + "book_receptacle", "crystal_block", "crystal_portal")) { + String path = "assets/rivencraft/models/block/" + model + ".json"; + try (InputStream stream = resource(path)) { + helper.assertTrue(stream != null, "Missing packaged block model " + path); + JsonObject json = JsonParser.parseReader(new InputStreamReader(stream, StandardCharsets.UTF_8)) + .getAsJsonObject(); + if (!json.has("textures")) continue; + for (var entry : json.getAsJsonObject("textures").entrySet()) { + String texture = entry.getValue().getAsString(); + if (!texture.startsWith("rivencraft:")) continue; + String texturePath = "assets/rivencraft/textures/" + + texture.substring("rivencraft:".length()) + ".png"; + helper.assertTrue(Milestone7ResourceGameTests.class.getClassLoader().getResource(texturePath) != null, + "Model " + model + " references missing texture " + texturePath); + } + } catch (java.io.IOException exception) { + throw new IllegalStateException("Cannot inspect " + path, exception); + } + } + helper.assertTrue(Milestone7ResourceGameTests.class.getClassLoader() + .getResource("data/rivencraft/structure/mystcraft_library.nbt") != null, + "The Legacy library template must be included in the mod JAR"); + helper.succeed(); + } + + private static InputStream resource(String path) { + return Milestone7ResourceGameTests.class.getClassLoader().getResourceAsStream(path); + } +}