Skip to content

Ready for Release 2.0.2 - #106

Closed
dshadowwolf wants to merge 14 commits into
master-2.0-1.21.1-neofrom
release-2.0.2
Closed

dshadowwolf wants to merge 14 commits into
master-2.0-1.21.1-neofrom
release-2.0.2

Conversation

@dshadowwolf

Copy link
Copy Markdown
Collaborator

MMDLib 2.0.2

Brings master-2.0-1.21.1-neo up to 2.0.2. It builds on the machine runtime expansion merged in #105 and adds:

  • data-driven sounds, creative tabs and standalone fluids;
  • the removal of the vanilla extensions;
  • a much larger CraftTweaker integration, with generated docs for CraftTweaker's wiki.

The branch is rebased onto the #105 merge, so this diff is only the 2.0.2 work. CHANGELOG.md has the full 2.0.2 section, including a summary of #105.

Highlights

Build and platform

  • NeoForge 21.1.265 (from 21.1.247), now the minimum version. Gradle wrapper 9.9.1.
  • Spotless stays at 8.9.0 on purpose.

Vanilla extensions removed (breaking for worlds)

  • The fifteen bundled materials are gone, along with their tools, armor, blocks, ammunition, dusts, gears, molten fluids, art, recipes, loot and tags. They now live in BaseMetals 4.0.
  • Existing worlds lose items and blocks with those mmdlib: IDs when updated.
  • The release jar no longer ships the generator for that content, which ran an external Python script.
  • MMDLib's creative tab is renamed "MMDLib".
  • The human detector's recipe needs #c:gears from another mod, such as BaseMetals.

Data-driven content

  • Sound events: mmdlib/sound/<path>.json, with an optional fixed hearing range.
  • Block sound sets: mmdlib/sound_type/<path>.json. A block's sound, or a material's block_sound, can name one.
  • Sounds wired in: screen elements take a click-sound. XML machine processors take a looped running-sound and a finish-sound.
  • Creative tabs: mmdlib/creative_tab/<path>.json, with ordered contents (items, #tags, whole materials), a translated title, an icon, placement and a search bar. CreativeTabDefinition.builder() does the same in Java.
  • Standalone fluids: mmdlib/standalone/fluid/<path>.json declares a fluid family (fluid, flowing form, liquid block, bucket) with no material. A new plain mmdlib:fluid module sits beside mmdlib:molten.
  • Other mods' content: they queue their own namespace's sounds, tabs and fluids on their RuntimeContentRegistrar, as with materials.

XML machines and screens (data stays the definition)

  • Cycle events: a <processor> can name on-start and on-finish handlers. The new machine-cycle-start and machine-cycle-finish events carry the machine, level, position, machine ID, recipe and cycle length. Handler IDs are checked when machines load.
  • Screen value providers: value="@namespace:id" (or max) reads a registered provider. It's type-checked against the element at load, like a getter.

CraftTweaker

All of this is in compat/CraftTweaker, with one gated call site in MMDLib's constructor.

#loader mmdlib startup scripts

  • CreativeTabBuilder, SoundBuilder and SoundTypeBuilder build new tabs and sounds or edit ones declared in files (edit, exists, declared).
  • FluidBuilder builds standalone fluids, with generated bucket models, blockstates and names.
  • ScreenHandlers (click and hover), ScreenValues (whole, decimal, flag, text) and MachineHandlers (start and finish) register functions under mmdlib: IDs that screen and machine XML name.
  • RecipeGenerators registers a machine recipe generator. Machine XML runs it through ScriptRecipeGenerator with script="mmdlib:<name>". Each reload it reads the reload's tags and the generator's attributes. The generator class has no CraftTweaker imports, so it fails with a plain message when CraftTweaker is absent.

Recipe scripts

  • XML machine recipe types work with CraftTweaker's own <recipetype:...> managers.
  • The typed MachineRecipeBuilder covers item, fluid, fluid-tag and energy inputs, reusable (consumed: false) inputs, outputs with chances, and time. It checks groups against the machines that use the type, as recipe reloads do.
  • A recipe handler for machine recipes makes /ct recipes dumps print them as builder calls. CraftTweaker's Replacer takes them apart and rebuilds them, keeping each part's group, count, consumption and chance.

Docs for CraftTweaker's wiki

  • All 53 script types carry @Document, and every member has a description, parameters, return values and @docParam examples.
  • Compiling writes the wiki pages (docs.json plus one page per type, under Mods › MMDLib) to build/crafttweaker-docs.

For mods built on MMDLib

Records with new components. The earlier constructors still exist.

  • MachineDefinition gained sounds and events, on top of Add multiblocks, dynamic machine visuals, and recipe controls #105's components. The 2.0.x constructor remains, and constructors without the new values use Sounds.NONE and Events.NONE.
  • DefinitionLoader.Loaded, MaterialExtensions.Definitions and Common.Clicks also have new components.

Fluid IDs.

  • FluidBuilder.setRegistryName fixes a fluid's ID, as for blocks and items.
  • MMDFluid.flowingId() puts flowing_ on the last path segment (test/flowing_syrup for test/syrup). Every material fluid's ID is unchanged.

New public API.

  • RecipeProcessor.startedCycles(), cycle() and justFinished().
  • ScreenValueProviders, plus a ScreenBinder.bind overload that takes a provider registry.
  • GeneratedMachineRecipes.validateGroups.

Removed. StaticAssetRules, which only served the removed artwork.

Build and tooling

  • The CraftTweaker annotation processor writes the wiki docs to build/crafttweaker-docs.
  • The formatter keeps @param name description on one line, which the docs processor needs. Files the formatter touched under this rule changed only in their Javadoc.
  • The Java API Javadoc excludes compat/CraftTweaker, which is documented for the wiki instead.

Testing

  • ./gradlew check passes: unit tests, both game-test suites (MMDLib 106, companion 101), Spotless, the line-limit warning task and the static-asset checks. ./gradlew javadoc passes.
  • New game tests cover:
    • sound events and sound sets, from data and scripts;
    • creative tabs, from data and scripts;
    • standalone fluids from data and scripts, including placing and picking up with buckets;
    • script screen handlers and value providers;
    • machine cycle events run through real cycles;
    • script machine recipes: typed, JSON, removed, reusable, and refused for a bad group;
    • a Replacer rebuilding a datapack machine recipe;
    • the recipe handler's dump and a full decompose and rebuild round trip;
    • a script recipe generator running in a test machine.
  • Every documentation page was checked for completeness.

Known issues

These are listed in the changelog and being fixed by #105's author. Update that section if the fixes land before tagging.

  • Multiblock members must keep exactly their authored block state, so a state change (connections, waterlogging, lit) breaks the structure, and under REMOVE_OWNED removes it.
  • Dismantling skips members in unloaded chunks, which keep stale links.
  • Changing a multiblock definition's version or policy leaves existing controllers unable to form again.

Multiblocks remain an early proof of concept, and their API will change.

🤖 Generated with Claude Code

dshadowwolf and others added 14 commits November 12, 2026 12:45
Vanilla extensions (now in BaseMetals 4.0):
- Remove the fifteen bundled materials with their art, generated recipes,
  loot tables and tags, the vanilla datagen provider (which ran an external
  Python script from the release jar) and its scripts, and StaticAssetRules.
- Keep the library's own assets, recipes, tags and the generic data run.
- Game tests use five materials, a gear tag and a crushing recipe as
  runtimeTest fixtures, with counts updated to match.

Sounds:
- mmdlib/sound/<path>.json registers sound events, with an optional fixed
  range; RuntimeContentRegistrar.queueSound and MMDLib.sounds().
- mmdlib/sound_type/<path>.json declares block sound sets over a vanilla
  base; a block's sound or a material's block_sound can name one.
- Screen elements take click-sound; XML machine processors take
  running-sound (looped on clients while running) and finish-sound.

Creative tabs:
- mmdlib/creative_tab/<path>.json describes a tab: ordered items, #tags and
  materials (each item once), title with fallback, icon, before/after
  placement and search bar. CreativeTabDefinition.builder() for Java;
  RuntimeContentRegistrar.queueTab and MMDLib.creativeTabs().

Docs, changelog (including the NeoForge 21.1.265 and Gradle 9.9.1 updates)
and tests for each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- mods.mmdlib.CreativeTabBuilder: new mmdlib: tabs, and edit(id) to change
  a tab declared in an mmdlib/creative_tab file (any namespace), which
  replaces the file's version. Properties for every tab field, chainable
  add/remove/clear for items, tags and materials, and exists/declared to
  read the file tabs. Errors point at the script line.
- Definitions carries creative tabs (old constructors kept), and
  MaterialExtensions.creativeTabFiles() exposes the file tabs while
  definition sources run; a source's tab with a file tab's ID replaces it,
  and two sources supplying one tab fail with both named.
- Tests: a test script building and editing tabs with game tests for both,
  and a loader unit test for replacement and duplicates.
- Docs: crafttweaker.md section, API inventory entry, creative-tabs.md,
  roadmap and changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- mods.mmdlib.SoundBuilder (range) and mods.mmdlib.SoundTypeBuilder (base,
  break/step/place/hit/fall sounds, volume, pitch): new mmdlib: IDs, and
  edit(id) to change one declared in an mmdlib/sound or mmdlib/sound_type
  file (any namespace), replacing the file's version; exists/declared read
  the file definitions.
- A material's blockSound now accepts a sound set ID, as a block's sound
  already did; unknown sets still fail at startup, naming the material.
- Definitions carries sound events and sound sets (old constructors kept);
  MaterialExtensions.soundFiles() and soundTypeFiles() expose the file
  definitions while sources run. The loader merges sounds, sound sets and
  creative tabs with one function: a source replaces a file's definition,
  two sources supplying one ID fail with both named.
- Tests: a test script creating and editing sounds and sets, with game tests
  for each, and a loader unit test.
- Docs: crafttweaker.md Sounds section, API inventory entries,
  definition-loading.md note, changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- mods.mmdlib.ScreenHandlers.onClick(name, fn) / onHover(name, fn) register
  gui-click / gui-hover callbacks as mmdlib:<name>, for screen XML (machine
  screens included) to name in on-click and its variants or on-hover; exists
  checks an ID. Screens stay XML; their handler IDs are validated at startup,
  after scripts run. Script failures are logged, not thrown.
- ScreenClickContext (player, block entity, level, pos, screen, element,
  button, shift/control/alt, option) and ScreenHoverContext (client side).
- Test script and a game test dispatching a real menu click to it.
- Docs: crafttweaker.md Screen handlers section, gui-screens.md pointer, API
  inventory, changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- value/max in screen XML can name a provider as @namespace:id;
  ScreenValueProviders.GLOBAL.register(id, valueType, blockEntity -> value)
  registers one (both sides, frozen when screens load). ScreenBinder checks
  its value type against the element and names the file and line for an
  unknown or wrong-kind provider; a new bind overload takes an explicit
  registry. ValueRef gains the Provided form; the XSD accepts it.
- mods.mmdlib.ScreenValues.whole/decimal/flag/text register mmdlib:<name>
  providers from scripts; failures are logged once and show no value.
- Tests: loader unit test for binding and rejection, test script and a game
  test reading each provider from a placed machine.
- Docs: gui-screens.md Values, crafttweaker.md Screen values, API inventory,
  changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New machine-cycle-start / machine-cycle-finish events (MachineCycle
  context: machine, level, position, machine ID, recipe, cycle length), run
  on the server when a new cycle starts and when one finishes with its
  outputs placed. A <processor> names handlers in on-start / on-finish;
  MachineContent checks each ID and its event when machines load.
- RecipeProcessor exposes startedCycles(), cycle() and justFinished();
  DefinedMachineEntity compares counters around each tick to fire the events
  and the finish sound. MachineDefinition gains an events component.
- mods.mmdlib.MachineHandlers.onStart / onFinish register mmdlib:<name>
  handlers from scripts, with a MachineCycleContext.
- Tests: an unpowered runtime-test XML machine naming a test script's
  handlers, run through real cycles in a game test; parse unit test.
- Docs: xml-machines.md, crafttweaker.md Machine handlers, API inventory,
  changelog (including the MachineDefinition API note).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- XML-declared machine recipe types already work with CraftTweaker's
  generic <recipetype:...> manager (addJsonRecipe, removeByName, ...),
  since each registers a serializer with its own ID; now tested and
  documented.
- mods.mmdlib.MachineRecipeBuilder (default crafttweaker loader): item,
  fluid and energy inputs per group, item and fluid outputs with chances,
  and time, built through the Java MachineRecipeBuilder and added with
  CraftTweaker's ActionAddRecipe.
- Tests: a recipe script adding (JSON and typed) and removing recipes for
  the runtime-test XML machine, with a game test running each.
- Docs: crafttweaker.md Machine recipes, xml-machines.md pointer, API
  inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- MachineRecipeBuilder.build() checks a script recipe's groups against
  every machine using its type, as recipe reloads do, and refuses it
  with the machine and group named. Script recipes are added after the
  reload, so they skipped this check; with #105's ghost groups, a recipe
  asking for more samples than slots would load and never run.
  GeneratedMachineRecipes.validateGroups is public for this.
- inputItems and inputFluid take an optional consumed flag, matching
  #105's "consumed": false recipe JSON.
- Tests: a reusable-input script recipe run in a game test, and a recipe
  with an unknown group that must be refused.
- Docs: crafttweaker.md machine recipes, API inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pt recipe generators

Standalone fluids:
- mmdlib/standalone/fluid/<path>.json declares a fluid with its own ID and
  no material, from a fluid module with optional template, tint,
  temperature, density and viscosity overrides: fluid, flowing form,
  liquid block and bucket, registered through the material fluid path.
  MMDLib.standaloneFluids() lists them; Definitions and Loaded carry them
  (old constructors kept).
- A plain water-like mmdlib:fluid module beside mmdlib:molten.
- FluidBuilder.setRegistryName fixes a fluid's ID (block and bucket
  follow); MMDFluid.flowingId() prefixes the last path segment. Material
  fluid IDs are unchanged.
- mods.mmdlib.FluidBuilder builds them from #loader mmdlib scripts, with
  generated bucket models, blockstates and names.

CraftTweaker machine recipe handler:
- /ct recipes dumps machine recipes as MachineRecipeBuilder calls, and
  Replacer decomposes and rebuilds them: CraftTweaker's built-in
  components plus mmdlib:machine/energy and mmdlib:machine/layout, which
  keeps each part's group, count, consumption and chance.
- MachineRecipeBuilder.inputFluids takes a fluid tag or several fluids.

Script recipe generators:
- Machine XML names ScriptRecipeGenerator with script="mmdlib:<name>";
  a #loader mmdlib script registers the function with
  mods.mmdlib.RecipeGenerators.register. It reads the reload's item tags
  and the generator's attributes and adds recipes. The generator class
  and its registry have no CraftTweaker imports.

All CraftTweaker code stays in compat/CraftTweaker; no machine or GUI
code changed.

Tests: loader and asset unit tests; game tests for a data fluid, script
fluids, Replacer on a datapack recipe, the handler's dump and a full
decompose/recompose round trip, and a script-generated recipe run in a
test machine. Docs: definition-loading, crafttweaker, xml-machines, API
inventory, parity plan; changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Every @ZenRegister type in compat/CraftTweaker carries
  @document("mods/MMDLib/<area>/<Type>"), and compiling writes CraftTweaker's
  docs pages (docs.json plus one JSON page per type) to
  build/crafttweaker-docs through its annotation processor.
- Every script member has a description, @PARAM, @return and @docParam
  example values, with @docParam this receivers on instance types.
  Failure conditions move from @throws into the descriptions, and class
  <pre> examples are dropped (the wiki builds its own from @docParam; full
  scripts stay in docs/crafttweaker.md).
- The formatter keeps "@PARAM name description" on one line: the
  processor reads the parameter name to the line end. Files the ratchet
  reformatted under it change only in Javadoc.
- The Java API Javadoc excludes compat/CraftTweaker, whose Javadoc holds
  script examples such as <item:...> brackets.
- Docs: "Generated API docs" in crafttweaker.md, a code-format.md line on
  @PARAM; changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GUI smoke test runs without CraftTweaker but loads
src/runtimeTest/resources, which held two test machines that need the
CraftTweaker test scripts: test/script_machine names on-start/on-finish
handlers only the scripts register (checked at startup, so the client
crashed during mod loading and CI hung until the step timed out), and
test/generated_machine uses the script recipe generator.

Those machines and their datapack recipes move to
src/runtimeTest/scriptResources, which only the game-test source set
loads.

Verified: ./gradlew check (game tests 106 and companion 101) and the GUI
smoke test with and without JEI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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