Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5af765d
test: match definition-loader messages on Windows paths
faceofcat Oct 31, 2026
f742165
docs: machine module design and current machine docs
faceofcat Oct 31, 2026
4f85ade
feat(machine): expression parser and core import rule
faceofcat Oct 31, 2026
10e0f5c
feat(machine): stat definitions and the modifier pipeline
faceofcat Oct 31, 2026
719987e
feat(machine): internals definition codec
faceofcat Oct 31, 2026
2261ee8
feat(machine): part files with material templates
faceofcat Oct 31, 2026
3e63032
feat(machine): part-file reload listener and wear component
faceofcat Oct 31, 2026
3cf36f3
feat(machine): machine internals with sockets, stats, wear and swap r…
faceofcat Oct 31, 2026
c173d9f
feat(machine): machine ingredients and fluid amounts
faceofcat Oct 31, 2026
9f0feac
feat(machine): slot groups, machine inventory and snapshot transactions
faceofcat Oct 31, 2026
3501044
feat(machine): machine products and recipe record
faceofcat Oct 31, 2026
d9980b3
feat(machine): recipe matcher with all-or-nothing consume and produce
faceofcat Oct 31, 2026
3e4bf5c
docs(machine): document stage 1
faceofcat Oct 31, 2026
24a4cf3
fix(machine): round-trip encoded expressions and nest saved sockets
faceofcat Oct 31, 2026
6a76692
fix(machine): refuse outputs with both item and fluid; test loader an…
faceofcat Nov 1, 2026
de88d10
docs: move machine design docs to the private design repo
faceofcat Nov 2, 2026
254fb23
chore: keep agent instructions out of the repo
faceofcat Nov 2, 2026
9d2ac5e
fix(machine): share inputs between ingredients instead of claiming gr…
faceofcat Nov 2, 2026
4e5cb17
fix(machine): report part file typos when a datapack loads
faceofcat Nov 2, 2026
f161169
fix(machine): tighten input checks and report load as one change
faceofcat Nov 2, 2026
b0496e2
chore: drop local ignore rules from .gitignore
faceofcat Nov 2, 2026
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
10 changes: 6 additions & 4 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ is checked every tick, keep a NeoForge `BlockCapabilityCache` instead.

## Notes

- **Planned: side configuration per resource group.** The GUI design configures sides per
group of slots or tanks (item input, item output, a tank, energy), Industrial Foregoing
style. Today a `SideConfig` applies to a whole storage; views will gain a per-slot (and
per-tank) mode so each group can have its own `SideConfig` without changing slot numbering.
- **Side configuration per resource group.** The screen's `<side-config>` view edits one
`SideConfig` per group (item input, item output, a tank, energy), Industrial Foregoing style;
the block entity returns them from `ScreenHost.screenSides()`. A view still takes one mode per
face for the whole storage, so the mod's `sides` function decides how groups combine. When
groups differ by `Role`, take insert from the input group and extract from the output group.
A per-slot mode in the views, which would remove that step, is not implemented.

- `ItemStorage` keeps the slot count given to its constructor. Loading a save that has more
slots drops the stacks that no longer fit. `FluidStorage` loads its tanks by index.
Expand Down
18 changes: 12 additions & 6 deletions docs/gui-screens.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,13 @@ show empty filters. No real items move through this path.

Bind the block's `block-right-click` event to `mmdlib:open_menu`. This handler first looks for an
XML screen matching the clicked block entity's type; otherwise it uses the block's own vanilla
menu provider. It consumes matching clicks on both sides and opens only on the server. A direct
code caller may use `ScreenRuntime.open(player, blockEntity)`.
menu provider. It consumes matching clicks on both sides and opens only on the server. A click
holding a fluid container on a face that has a fluid handler is left alone, so the block's own
item use can fill or empty the tank; MMDLib's `MaterialEntityBlock` does not do that itself. A
direct code caller may use `ScreenRuntime.open(player, blockEntity)`.

`screenSides()` keys must equal the element `id`s named in `<side-config groups="...">`; a group
with no entry in the map is listed, but shows no modes and ignores clicks.

Click extension methods use event type `gui-click` and signature `public static void method(ScreenClick click)`.
The context supplies the player, menu, element, mouse button, Shift/Control/Alt state, and selected
Expand Down Expand Up @@ -160,10 +165,11 @@ a compact meter, a few controls, an upgrade tab, and a side-configuration view:

<view id="sides" title="screen.mmdlib.side_config">
<side-config groups="input output water energy"/>
<radio-group label="screen.mmdlib.redstone" value="#getRedstoneMode" on-click="mmdlib:set_redstone_mode">
<radio text="screen.mmdlib.redstone.ignore"/>
<radio text="screen.mmdlib.redstone.high"/>
<radio text="screen.mmdlib.redstone.low"/>
<!-- Redstone control is the mod's own: MMDLib ships no handler or keys for it. -->
<radio-group label="screen.example.redstone" value="#getRedstoneMode" on-click="example:set_redstone_mode">
<radio text="screen.example.redstone.ignore"/>
<radio text="screen.example.redstone.high"/>
<radio text="screen.example.redstone.low"/>
</radio-group>
</view>
</context>
Expand Down
7 changes: 7 additions & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@

These are flagged for consideration, not committed work.

### Machine module

Machine ingredients and recipes across items, fluids and energy, transactions, a processing
loop, then machines defined entirely in JSON; also the plan for NeoForge 26.1 and Fabric 26.3
builds. Stage 1 is built (internals without GUI, and ingredients, groups, transactions and
matching).

### KubeJS and CraftTweaker recipe integrations

Optional polish over the generic support above, each loaded only when that mod is present: a
Expand Down
15 changes: 15 additions & 0 deletions gradle/verification.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def builderChecks = [
verifyMethodEvents : ['MethodEventsTest'],
verifyDefinitionLoader : ['DefinitionLoaderTest'],
verifyScreens : ['zone.moddev.mc.lib.gui.ScreenLoaderTest'],
verifyMachineCore : ['zone.moddev.mc.lib.machine.MachineCoreTest'],
verifyVanillaExamples : ['VanillaExamplesTest', ['mmdlib.resources': resources,
'mmdlib.generated': file('src/generated/resources').absolutePath]],
]
Expand All @@ -58,6 +59,20 @@ def verifyReleaseClasses = tasks.register('verifyReleaseClasses') {
}
tasks.named('check') { dependsOn verifyReleaseClasses }

// The machine module's core stays loader-neutral: only machine/neoforge may import NeoForge.
def verifyMachineImports = tasks.register('verifyMachineImports') {
group = 'verification'
def root = file('src/main/java/zone/moddev/mc/lib/machine')
def sources = fileTree(root) { include '**/*.java'; exclude 'neoforge/**' }
inputs.files(sources)
doLast {
def leaked = sources.files.findAll { it.text =~ /(?m)^import\s+(static\s+)?net\.neoforged\./ }
if (!leaked.isEmpty())
throw new GradleException('Machine core files import NeoForge: ' + leaked.collect { root.relativePath(it) })
}
}
tasks.named('check') { dependsOn verifyMachineImports }

def verifyStaticAssets = tasks.register('verifyStaticAssets', Exec) {
group = 'verification'
dependsOn tasks.named('classes')
Expand Down
7 changes: 7 additions & 0 deletions scripts/runtime-smoke.init.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ online-mode=true
level-name=runtime-smoke
spawn-protection=0
'''
// A real datapack in the smoke world, so reload listeners read files the way packs ship them.
def datapack = new File(smokeDir, 'runtime-smoke/datapacks/mmdlib_smoke')
project.delete(datapack)
project.copy {
from file('src/runtimeTest/datapack')
into datapack
}
runs.server.workingDirectory = smokeDir
// NeoGradle creates run tasks in a later evaluation callback.
tasks.configureEach { task ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ private static void expectFailure(Runnable action, String... fragments) {
try {
action.run();
} catch (IllegalArgumentException ex) {
for (var fragment : fragments) require(ex.getMessage().contains(fragment));
// Messages name file-system paths, which use backslashes on Windows.
var message = ex.getMessage().replace('\\', '/');
for (var fragment : fragments) require(message.contains(fragment));
return;
}
throw new AssertionError("Bad definitions accepted");
Expand Down
Loading