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
14 changes: 11 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ apply from: 'gradle/line-limit.gradle'

def env = System.getenv()
def tag = providers.environmentVariable('TAG')
version = tag.orElse("${mod_version}-development").get()
def localMavenRepo = providers.gradleProperty('mmdlibLocalRepo').filter { !it.isBlank() }
def useLocalMaven = providers.gradleProperty('mmdlibUseLocal').map { it.toBoolean() }
.getOrElse(true) && localMavenRepo.isPresent()
def developmentVersion = useLocalMaven
? providers.gradleProperty('mmdlibLocalVersion').getOrElse("${mod_version}-development")
: "${mod_version}-development"
// Release tags always win over machine-local development settings.
version = tag.orElse(developmentVersion).get()
group = mod_group

base {
Expand Down Expand Up @@ -173,10 +180,11 @@ publishing {
}
}
}
// Local inspection: `./gradlew publishAllPublicationsToLocalRepository` writes to mcmodsrepo/.
// Shared dev repository from ~/.gradle/gradle.properties; defaults to mcmodsrepo/.
// Publish only locally with: ./gradlew publishAllPublicationsToLocalRepository
maven {
name = 'local'
url = layout.projectDirectory.dir('mcmodsrepo')
url = useLocalMaven ? uri(localMavenRepo.get()) : layout.projectDirectory.dir('mcmodsrepo')
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,22 @@ is checked every tick, keep a NeoForge `BlockCapabilityCache` instead.
- The runtime smoke test covers roles, filters, limits, faces, out-of-range indices, transfer,
NBT round trips, int clamping, and the face mapping. Real cables and pipes still need testing
in game.

### Physically unavailable faces

Use `new SideConfig(IoMode.BOTH).disabledFaces(Set.of(RelativeFace.RIGHT))` for a face that
must never expose this resource. All faces are available by default. Restrictions are relative
to the controller front and rotate with it; RIGHT means the viewer's right while looking at
the front (west for a north-facing controller).

This is machine policy, distinct from a player's Disabled mode. `faceAvailable(face)` reports
it; `get`, `set`, `cycle`, and `copyFrom` enforce it. Restore decoded modes into the existing
policy-bearing instance. The codec saves modes only, not restrictions. Changing the mask
notifies the owner once, and re-enabling a face leaves its mode Disabled. AutomationInventory
also advances its view revision and invalidates capability caches on side changes.

Retained handlers must still consult the current policy: use `items.view(slot ->
sides.mode(facing(), side))` and `energy.view(() -> sides.mode(facing(), side))`, intersected
with each resource's supported direction. Passing a mode value creates a fixed view and cannot
track future changes. Active transfers must consult the same policy. Null-side/internal access
remains host-owned; a machine may refuse it independently of its six physical faces.
25 changes: 23 additions & 2 deletions docs/gui-screens.md
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,8 @@ submission path, including pending-request handling. The server dispatcher check
request order, reach and visibility before decoding. The ghost action delegates to the inventory
component; `InventoryElement.Session` resolves the named inventory and rejects inactive,
unregistered or non-ghost slots. The old `ScreenNetwork.Ghost` packet and `MachineMenu.ghost`
entry point are removed. Core screen protocol 20 requires matching client/server versions.
entry point are removed. Core screen protocol 21 requires matching client/server versions;
menu opening now carries a host-validated physical interaction anchor for multiblocks.

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
Expand Down Expand Up @@ -956,7 +957,7 @@ payloads, codecs and handlers. `ScreenRuntime` only installs the registry; the g
network has no list of feature networks. NeoForge continues to negotiate versions and dispatch
payloads on its normal handler thread. Handlers remain responsible for menu/session identity,
authorization, revisions and rate limits. Each feature declares its own version; the core screen
protocol is 19, and automation, redstone and container drawers remain at version 16.
protocol is 19, and automation and redstone drawers use version 17 (unavailable-face masks).

### Registered menu components and native slots

Expand Down Expand Up @@ -2562,3 +2563,23 @@ None.

- **Meter extras:** colour zones (VU style) and peak hold (EQ style). Left out to keep the
first version simple.

### Unavailable resource and redstone faces

`SideConfig.disabledFaces(Set<RelativeFace>)` and `RedstoneControl.disabledFaces(Set<RelativeFace>)`
set machine-owned restrictions independently. Apply both when a physical obstruction blocks
both resource access and redstone. The side drawer, inline `<side-config>`, and redstone drawer
retain all six tiles but show unavailable faces as a centered gray X at half the tile size,
without neighbor models or names. They explain the restriction on hover,
and refuse edits. Redstone selection moves to an available face; all-unavailable configurations
hide editing controls. Server validation also rejects stale or forged edits to blocked faces.

Redstone restrictions prevent sampling, interval matching (including Outside), output emission,
and dust connections. Modes normalize to Ignore, including after loading or legacy migration;
re-enabled faces remain Ignore. Disk saves contain player settings only. Hosts outside BaseMachine
must use `saveClient`/`loadClient` for world updates so client dust geometry receives policy.
BaseMachine does this automatically. Normal disk persistence still uses `save`/`load`.

Automation and redstone drawer protocols are version 17 with bounded unavailable-face masks.
Inline controls synchronize a separate `mmdlib:side_unavailable` field. Existing machines default
to all faces available and need no policy declaration.
40 changes: 40 additions & 0 deletions docs/local-development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Using MMDLib from a local Maven repository

MMDLib can publish the working checkout to a shared folder without pushing or
waiting for CI. Configure the folder and development version in the Gradle user
home `gradle.properties` (normally `%USERPROFILE%\.gradle\gradle.properties` on
Windows, or `$GRADLE_USER_HOME/gradle.properties` when customized):

```properties
mmdlibLocalRepo=Z:/maven
mmdlibLocalVersion=2.0.0.121012-development
```

Publish locally with:

```powershell
.\gradlew.bat publishAllPublicationsToLocalRepository
```

This task publishes the main jar, sources, Javadoc and Maven metadata. It does not
publish to the remote repository. Without `mmdlibLocalRepo`, the local destination
remains `mcmodsrepo/` in this checkout. Without a local version override, the
development version remains `${mod_version}-development`. The `TAG` environment
variable always takes precedence for release builds.

Consumer projects can read the same two properties to select the local repository
and dependency `zone.moddev.mc.lib:mmdlib:<mmdlibLocalVersion>`. When local settings
are absent, use `https://maven.moddev.zone/releases` and the consumer's pinned release version.
Declare the selected repository as exclusive for the MMDLib module so a missing
local artifact cannot silently fall back to a remote build. Mark the local
dependency as changing and set its changing-module cache lifetime to zero, since
successive local publishes reuse the same version.

Republish after editing MMDLib, then rebuild or restart the consumer's development
game. The source checkout can contain uncommitted changes. A Maven dependency does
not automatically rebuild the source checkout.

`-PmmdlibUseLocal=false` disables the local overrides in MMDLib; consumers using
this convention can use the same flag to verify their pinned remote dependency.
Keep machine-local settings outside project repositories. Release tags and remote
publishing credentials continue to come from CI.
9 changes: 8 additions & 1 deletion 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 six playable machine examples.
The optional `mmdlib_companion` mod contains playable machine examples, including a multiblock fixture.
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 @@ -39,6 +39,13 @@ those mods' login payloads; real client smoke runs include them.
| `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 |
| `multiblock_demo` | One item places a controller and two linked parts; the far part exposes item/fluid/energy ports; breaking a member dismantles the machine |

The **Multiblock Test Bench** is in the Machines tab. It occupies the controller position and two
positions east of it when facing north, and rotates with placement. Keep the block above the middle
part clear; obstructing it suspends its active-tick counter and resource ports. Clicking any part
opens the controller screen. Ordinary survival breaking drops one machine item and its item
contents. See [multiblocks](multiblocks.md) for the reusable API and registration example.

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
Expand Down
6 changes: 6 additions & 0 deletions docs/mod-author-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
This guide targets this checkout of MMDLib on **NeoForge 1.21.1 / Java 21**.
The example material is `example:orichalcum`. Substitute your mod ID for `example`.

For building a consuming mod against your working checkout without waiting for CI,
see [local Maven development](local-development.md).

## What “all features” currently means

A material JSON file describes item, block, projectile, block-entity, and fluid
Expand All @@ -20,6 +23,9 @@ including inherited storage, generated screens and dynamic recipes. More special
machines and custom world entities use their dedicated typed factory APIs.
The manual wiring below remains an alternative; do not queue objects twice.

Java machines spanning several blocks can use [multiblock placement and lifecycle](multiblocks.md),
with one controller, linked physical parts and guarded resource ports.

## 1. Lay out the data

A useful layout in the consuming mod is:
Expand Down
Loading
Loading