Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,13 @@ tasks.register('format') {
if (!jar.isFile())
throw new GradleException("google-java-format not found at ${jar}; see build.gradle")
// Imports are left in place here and grouped by the script below instead.
providers.exec {
commandLine([java, '-jar', jar.absolutePath, '--skip-sorting-imports', '--replace'] +
sources.files.collect { it.absolutePath })
}.result.get()
// Keep each invocation below Windows' command-line length limit as the library grows.
sources.files.toList().collate(40).each { batch ->
providers.exec {
commandLine([java, '-jar', jar.absolutePath, '--skip-sorting-imports', '--replace'] +
batch.collect { it.absolutePath })
}.result.get()
}
def grouping = providers.exec {
commandLine python, file('scripts/group_imports.py').absolutePath, file('src').absolutePath
}
Expand Down
12 changes: 12 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ index, or through the explicit `deserializeNBT(registries, tag, IntUnaryOperator
overload. Select that mapping by your machine's save version; it applies only to legacy entries.
Keep slot/tank IDs stable when reordering a definition.

For `BasePoweredMachine`, override `protected IntUnaryOperator legacySlots(String key,
CompoundTag tag)`. The key identifies the storage and `tag` is the complete world-save tag, so
the hook can read your saved version before `loadMachineData` runs. It defaults to identity
and applies to both items and fluids before features restore their reservations. Entries with
stable IDs bypass the mapping. Direct lifecycle users can pass the same resolver to `load`.

Storage save keys cannot use `automation`, `features`, `redstone_control`, `redstone_mode`,
`id`, `components`, `mmdlib_features`, or `mmdlib_machine`; those belong to persistence or sync.

Storage keeps its configured shape. Removed slots/tanks, overflow after reducing limits/capacity,
incompatible colliding contents and undecodable entries are retained in raw `Recovery` data,
instead of being discarded. Subsequent loads retry recovery entries, allowing returning IDs or
Expand All @@ -99,6 +108,9 @@ input rules are installed by [MachineResources](inventory-automation.md#shared-m
They supplement existing filters and reservations. Machine products use `insertResult` for items
or `fillResult` for fluids to bypass external admission rules; fluid products still obey the
tank's structural filter and capacity. Restoring a saved item explicitly can use `restoreStack`.
Recipe-linked filters index accepted items and fluids per recipe-manager snapshot and rebuild
after reload or client recipe synchronization. Component-sensitive custom ingredients still test
the actual stack on every probe; item identity alone cannot decide those matches.

## Example

Expand Down
8 changes: 8 additions & 0 deletions docs/definition-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ src/main/resources/mmdlib/
fluid/coolant.json
extension/armor_effects.json
material/copper.json
machine/simple_crusher.xml
```

IDs use the owning mod's namespace and the relative path below the section,
Expand All @@ -20,6 +21,13 @@ root-level directory belongs to the first mod declared in its metadata. Extensio
files also have file IDs, but their callable handler IDs come from the explicit
`id` fields in their `events` array.

Machine XML uses the same namespace/path rule, without `.xml`. It is loaded after
the other startup definitions and composed with the screen templates. See
[XML machines](xml-machines.md) for inherited storage, generated screen fills,
recipe generators and the complete SimpleCrusher example. The generic machine
runtime registers concrete definitions; abstract definitions provide reusable
configuration only.

The existing baseline under `data/mmdlib/{item,block,fluid}` remains loaded through
`ModuleCatalog.bundled()`. The startup scanner does not scan that catalog again or
reinterpret unrelated mods' data directories. `ModuleCatalog.load(packRoot)`
Expand Down
20 changes: 18 additions & 2 deletions docs/gui-screens.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ Repeated references to one getter are evaluated once per machine per tick, share
Navigation is checked on the server and its selection is synced back to the client. Opening also
compares a screen-model fingerprint, so differing XML definitions fail explicitly instead of
assigning values or actions to the wrong elements.
`ScreenHost.screenDefinitionFingerprint()` can contribute additional startup configuration;
XML machines supply their resolved definition CRC, including inherited storage settings.

`./gradlew check` includes numeric protocol and layout regression checks. The isolated graphical
smoke run is `xvfb-run -a ./gradlew runClient --offline -I scripts/gui-smoke.init.gradle`; repeat
Expand Down Expand Up @@ -479,6 +481,12 @@ Inheritance does not replace the type-ID screen lookup described above.
the named placeholder's contents; an empty fill removes its default content.
- An unfilled optional placeholder uses its children, or nothing when it has none. A required
placeholder must be filled and cannot also have default content.
- A placeholder may instead supply `generator="namespace:id"` and generator options.
An explicit fill replaces that generator, even when empty; a fill can choose its own
generator. Generated fills cannot also contain literal children. Generators return normal
screen elements, which undergo the same expanded validation. See
[machine screen generators](xml-machines.md#generated-screen-fills) for the available
machine-aware generators and registration API.
- Chains can introduce placeholders and override earlier fills. Replacing an outer placeholder
removes its nested extension points; also filling a removed inner point is an error.
- Unknown bases/fills, duplicate placeholders/fills and cycles fail at load. Expanded slots, IDs,
Expand Down Expand Up @@ -545,8 +553,11 @@ Icons show the current state, not the next action:
| Processor state | Icon | Tooltip lines / click |
|---|---|---|
| Manually paused | Pause | `Paused` / `Click to unpause` |
| Not paused and processing | Stop | `Working` / `Click to stop and clear reservations` |
| Not paused and not processing | Play | `Idle` / `Click to pause` |
| Processing | Stop | `Working` / `Click to pause and release reservations` / `Progress is kept` |
| Waiting for energy | Stop | `Needs power` / same pause action |
| Cannot complete the recipe | Stop | `Recipe cannot finish` / same pause action |
| Held by the host | Stop | `Held by machine controls` / same pause action |
| Idle | Play | `Idle` / `Click to pause` |

Tooltips are compact and multiline. Manual pause releases reservations, keeps resumable progress,
marks the machine changed and synchronizes immediately. Redstone/host holds preserve reservations
Expand Down Expand Up @@ -724,6 +735,7 @@ the block at a position), and that instance is what the getters are called on:
| Form | Resolves to | Called as |
|---|---|---|
| `#method` | A public instance method on the screen's `block-entity` class (or a superclass or interface of it), taking no parameters | `blockEntity.method()` |
| `#method(index)` | A public instance getter taking one fixed nonnegative `int`, e.g. `#getTankFluid(0)` | `blockEntity.method(index)` |
| `Class#method` | A public **static** method taking the block entity (or a supertype) as its only parameter, e.g. a helper shared by several machines | `Class.method(blockEntity)` |

Resolution happens in two steps. At load, each reference is looked up from the class and name
Expand All @@ -732,6 +744,9 @@ the screen, the server binds the handles to that menu's block entity, after chec
instance of the declared `block-entity` class. An instance method on any other class has no
instance to call and is rejected at load.

Indexed references support generated gauges for multiple tanks without one Java
getter per tank. For example, `value="#getTankFluid(0)" max="#getTankCapacity(0)"`.

Getters run only on the server, each tick while the screen is open. The client never calls
them; it only receives the values.

Expand Down Expand Up @@ -870,6 +885,7 @@ When JEI is installed:
offers, each naming the `id` of the element that shows it (a `<group>`, `<gauge>`, ...).
While a group is selected in the side-configuration view, that element's background is
highlighted when it is visible on the screen, so the player sees which slots or tank they are changing.
Highlight targets are optional: missing elements are skipped and do not prevent the menu opening.
Numbering is unaffected: every face still shows every slot, and a group's setting only
allows or refuses operations on its own slots.
- **Block-entity registration is a hybrid.** For a data-defined machine, MMDLib validates the
Expand Down
20 changes: 16 additions & 4 deletions docs/machine-companion.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Machine companion

The optional `mmdlib_companion` mod contains five playable machine examples.
The optional `mmdlib_companion` mod contains six playable machine examples.
It uses Minecraft 1.21.1, NeoForge 21.1.247 and this branch's MMDLib. Its classes and resources live
in `src/companion` and are packaged separately from the library.

Expand Down Expand Up @@ -38,12 +38,23 @@ those mods' login payloads; real client smoke runs include them.
| `dual_crusher` | Two independently pausable processors share the same input and output slots |
| `logistics` | Seven independent resource groups for testing automation, filters and side configuration; no recipe processing |
| `creative_power` | Refills its buffer and supplies up to 1,000 FE per enabled adjacent face per tick |
| `simple_crusher` | XML-only definition with inherited power, generated screen contents, and tag-generated ore-to-ingot recipes; in the **Data-defined Machines** tab |

The crusher recipes take 100 ticks and 2,000 FE per cycle: cobblestone becomes gravel, gravel becomes
sand, and copper/iron/gold ore becomes two of the corresponding raw material. The converter takes
200 ticks and 4,000 FE. The dual crusher runs the same recipes in both lanes when there are enough
inputs and output space.

SimpleCrusher converts one ore to two ingots. Its `.10` hardness scaling increases
the base 100 ticks / 2,000 FE to 130 ticks / 2,600 FE for regular iron ore.
Its recipe generator joins `c:ores/<metal>` and `c:ingots/<metal>` tags, including
metals added by other mods. It has no companion Java block or entity class. See
[XML machines](xml-machines.md) for the complete definition and generator contract.
For focused verification of this example, run `./gradlew runSimpleCrusherClientSmoke`.
It opens the generated screen, checks the model and synchronized recipes, reloads
server resources, and saves before/after screenshots under
`build/simple-crusher-client-smoke/screenshots/`.

The Crusher's two outputs share one inventory and colour/side configuration. For testing, both
allow insertion and extraction, so you can manually fill them with any item; reservations still
protect promised output space. New Crushers default this group to input/output on all faces. Outputs may
Expand All @@ -55,8 +66,9 @@ starting empty. The Dual Crusher keeps its existing shared single-output layout.
The bottom of each processor panel has a compact, right-aligned row of icon buttons above the inventory,
with thin dark borders and flat grey backgrounds.
The generic processor button shows its **current** state: pause when manually paused, stop while
working, play while idle. Click to unpause, stop and release reservations, or pause idle work,
respectively. Resumable progress is retained. The Dual Crusher targets each lane explicitly;
working or held, play while idle. Tooltips distinguish waiting for power, blocked completion and
host holds. Click to unpause, pause and release reservations, or pause idle work, respectively.
The pause tooltip explicitly says progress is kept. The Dual Crusher targets each lane explicitly;
pausing one releases only its share. Single-processor machines inherit a keyless button from the base.

Redstone face settings control whether processing may run. A redstone hold keeps progress and
Expand All @@ -70,7 +82,7 @@ Every screen inherits the yellow Info popout from `mmdlib:base_powered_machine`.
`{machineId}.info`, such as `mmdlib_companion:transmuter.info`, plus dynamic warnings from the base
entity. The base reports processor power stalls, blocked finishes and host holds; Crusher can also
report its configured energy reserve. Click the icon to close the drawer; scroll longer text.
All five screens place a vertical battery on the right using `<energy-gauge>`.
All six screens place a vertical battery on the right using `<energy-gauge>`.
Its red charge bars fill upwards inside a slim casing.
The energy and fluid gauges share matching edges and medium-grey empty backgrounds, darker than slots.
Energy bars are thin with a soft reflection; cylindrical glass shading covers only the fluid.
Expand Down
4 changes: 3 additions & 1 deletion docs/mod-author-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ available for mods that own their loading stage.
registries, including ammunition, armor, stateful blocks, fluid families, and
client hooks. MMDLib uses it for its [playable vanilla extensions](vanilla-extensions.md).
Mods must still supply their assets/recipes, or a generator appropriate to their
forms. Machines and custom world entities use their dedicated typed factory APIs.
forms. Powered item-processing machines can use [XML definitions](xml-machines.md),
including inherited storage, generated screens and dynamic recipes. More specialized
machines and custom world entities use their dedicated typed factory APIs.
The manual wiring below remains an alternative; do not queue objects twice.

## 1. Lay out the data
Expand Down
Loading
Loading