| Raw microblock recipe material lookup | Skips `MissingMicroMaterial`, whose recovery item is stone, so sawing stone selects the real stone material. Existing missing-material microblocks retain their recovery behavior. This fixes a pre-existing Scala bug, also fixed upstream in PR #61. |
| `BlockMicroMaterial.icont()` after a texture stitch | `loadIcons` still builds icons eagerly, then marks them stale; the first `icont()` read after the stitch re-reads the block's icons once. Icons that another mod finishes in a later `TextureStitchEvent.Post` handler (Chisel's CTMLib `-v4` submaps) are picked up instead of rendering as missing or previous-stitch textures. Icons assigned through `icont_$eq` are kept. Upstream PR #63 targets the same bug by lowering the stitch handler to `EventPriority.LOW`; this port does not need that ordering. |
| Client part additions | Reuses an existing multipart tile or creates one without converting the stale client block. Conversion and its outgoing packets remain server-side, avoiding duplicate parts and client disconnects. Matches upstream PR #62. |
| `BlockMicroMaterial.createAndRegister(Block, Seq, String)` | Registers every supplied metadata value instead of only zero, including dark-oak logs and leaves and their legacy-name remaps. This fixes a pre-existing Scala bug; material IDs are rebuilt through the existing registry mapping. |
| Chunk scheduled-tick restoration | Reactivates chunks with restored scheduled ticks so their callbacks resume at the saved deadlines. The Scala implementation restored entries without activating their chunk. NBT layout and duplicate-scheduling behavior are unchanged. |
| World scheduler clock loading | Retains successfully loaded `schedTime` instead of overwriting it with total world time. Missing or unreadable files still fall back to world time. This fixes a pre-existing Scala bug and preserves the clock used by saved absolute tick deadlines; the file format is unchanged. |
| Converted tile loading | Copies the source tile coordinates to a replacement produced by `MultipartSaveLoad.loadTiles` before assigning its world and validating it. The Scala implementation left converted replacements at their default coordinates. |
| Placement-modifier player lifecycle | Removes cached control-key state when a player logs out and transfers it from the old player object to its replacement on clone. The Scala implementation retained entries until server shutdown. |
| `IDWriter` before `setMax` | `read`/`write` throw `IllegalStateException` instead of `NullPointerException`. Uninitialized use is unsupported. |
| `PacketScheduler` invalid `maskWidth` | Throws `IllegalArgumentException` instead of `scala.MatchError`. Valid widths remain 1, 2, 4 and 8. |
| `NormalOcclusionTest.apply(Traversable, Traversable)` | Materializes both inputs into Java lists before testing instead of nested Scala `forall`. Finite, side-effect-free collections give the same result; eager traversal can change side effects, failure timing and termination for unusual inputs. |
| `MultipartRenderer` dynamic-part guard | Uses short-circuit `\|\|` instead of `\|`; the second getter is no longer evaluated when the first operand succeeds. Ordinary getters give the same render decision. |
| `ControlKeyModifer.map()` | The exposed Java map has no Scala default-value wrapper: an absent key returns null from `get`, not false from Scala `apply`. Use `isControlDown` for the unchanged absent-means-false behavior. |
| Public companion constructors, including `ASMImplicits.ExtBitSet$` / `ExtClass$` | Explicit construction creates an independent instance instead of replacing `MODULE$`. Java initializes the retained final module field in its static initializer; use `MODULE$` for singleton identity. |
| `registerJavaTrait` abstract inputs | Abstract Java classes are accepted instead of throwing `IllegalArgumentException`. Declared abstract methods become generated-interface contracts without helper bodies; a later mixin must implement them before those members can be invoked. A no-argument abstract-mixin constructor may call an argument-taking superclass constructor; registration discards that direct call and its argument evaluation because the composite invokes the real base constructor first. This enables Java microblock mixins without changing existing concrete registrations. |
| `registerJavaTrait` members annotated `@SideOnly` | The old Java path ignored the annotation. It now omits opposite-side fields and methods from generated metadata and output, matching Forge and the Scala-trait path; visible and invisible annotation tables are both accepted. If the no-argument constructor is absent, including after Forge has already stripped it, an empty `$init$` preserves the generated initializer contract without running its body. Current-side members are unchanged. No audited Java registration relied on the old retention. |
Code depending on concrete collection classes or their exact mutation failure behavior must adapt.
## Removed or changed binary surface
These removals were accepted because neither audit identified a consumer of the removed surface. This does not
authorize removing other members of the same type.
| Area | Removed or changed surface |
| --- | --- |
| `IDWriter` | Removed `write()`, `write_$eq(Function2)`, `read()` and `read_$eq(Function1)`. Use the Java methods listed below. |
| Both registries | `MicroMaterialRegistry.readIDMap` and `MultiPartRegistry$.readIDMap` return `java.util.List<String>` instead of `scala.collection.Seq<String>`; the return descriptor changes. |
| Registry compiler accessors | Removed `MicroMaterialRegistry`'s public mangled `$$idMap()`, `$$typeMap()` and `$$nameMap()` accessors, and the five `codechicken$multipart$MultiPartRegistry$$...()` accessors. Schematica's separate private `MultiPartRegistry$.$$typeMap`**field is retained** as a live Scala-map view; it is not one of the removed methods. |
| `NormallyOccludedPart` | Removed the inferred `getType(): scala.runtime.Null$` overload; the `String` method remains. |
| `MissingMicroMaterial` | Removed static `canRenderInPass(int)` and `isSolid()` forwarders. The companion no longer declares these methods but inherits the interface defaults. |
| `TileCache` | `map()` now returns Java `Map`; `add`/`remove` return `void` instead of Scala `Option`; `apply(BlockCoord)` returns a nullable `FlaggedTile` instead of `Option`. |
| `TileCache.FlaggedTile` | Dropped Scala case-class `copy`, copy-default getters, `canEqual`, value `equals`/`hashCode`/`toString`, product methods, and `scala.Product`/`scala.Serializable`. It now has ordinary object identity semantics; `t()` and `removed()` remain. |
| `ControlKeyModifer` | Replaced `isClientPressing_$eq(boolean)` with `setClientPressing(boolean)`; `map()` returns Java `Map<EntityPlayer, Boolean>` instead of Scala mutable `Map`; removed `playerControlValue(EntityPlayer)` and its implicit wrapper type. |
| `ControlKeyHandler` | Former static facade is now a `KeyBinding` subclass with `INSTANCE`. `wasPressed`/`tick` are instance methods, and the twelve inherited-KeyBinding static forwarders disappear. Initializing this client-only class now initializes its binding. |
| `TileMultipart` | `hasDynamicParts` (false by default), `renderStatic`, `renderDynamic` and `randomDisplayTick` hooks for dispatching generated overrides through a stable superclass |
| `MultipartMixinFactory` | Four additional static forwarders: `onCompiled`, `autoCompleteJavaTrait` and the two mangled `ASMMixinFactory` parent helpers. Scala emitted them when the base became Java with its original public JVM access; the Java facade retains them. Existing facade entries are unchanged. |
| `MultipartEventHandler` / `MultipartEventHandler$` | Public `playerLoggedOut(PlayerLoggedOutEvent)` and `playerCloned(PlayerEvent.Clone)` event handlers for placement-modifier state cleanup and transfer |
| `MultipartHelper` | `createPreviewTileFromNBT(World, NBTTagCompound)` builds an uninstalled client composite directly from saved multipart NBT and rejects unsupported parts as a whole |
| `MultiPartRegistry.IPartFactory2` | `createPartForClientPreview`; defaults to unsupported, while legacy Boolean/function adapters and built-in microblock factories construct their client variant |
`ItemSaw` consequently no longer declares `getMaxCuttingStrength`; ordinary calls still resolve to the default,
but declared-member reflection differs. The same distinction applies to the two `MissingMicroMaterial$` methods above.
The `TNormalOcclusion` super accessor
`codechicken$multipart$TNormalOcclusion$$super$occlusionTest(TMultiPart)` additionally loses `ACC_SYNTHETIC` and
becomes a default returning true, so javac can see and implement it. `NormallyOccludedPart` inherits it rather than
declaring its own. Existing Scala binaries retain their explicit super-chain overrides.
## Recompiling Scala consumers
Binary descriptors alone do not preserve Scala source semantics after `ScalaSignature` metadata is removed.
### Trait composition
A Java interface cannot supply a Scala trait's class supertype or its linearization. A superclass method wins over
an interface default, so recompiling `extends TMultiPart with TCuboidPart` silently selects the empty superclass
methods. Explicit forwarding is required where the implementation remains abstract:
| Trait | Source migration |
| --- | --- |
| `TCuboidPart` | Extend `JCuboidPart`, or forward subparts, collision boxes and breaking rendering to its statics. |
| `TNormalOcclusion` | Explicitly combine `NormalOcclusionTest.apply(this, other)` with the intended super chain. Audit existing `super.occlusionTest` calls too: they no longer route through the old trait. |
| `TIconHitEffects` | Explicitly forward `addHitEffects` and `addDestroyEffects` to `IconHitEffects`. |
| `TItemMultiPart` | Explicitly forward `onItemUse` to `JItemMultiPart`; the inherited Item method otherwise wins. |
| `TRandomUpdateTick` | Explicitly implement `onWorldJoin` with `TickScheduler.loadRandomTick(this)`. |
| `TScheduledPacketPart` | Explicitly forward `read` to `TScheduledPacketPart.readMask(this, packet)`. |
| `ScratchBitSet` | Implement both methods and storage accessors, and initialize storage explicitly; the retained `$class` helper supplies the original implementations. |
Previously compiled calls to **retained**`$class` bridges still link. This does not apply to the removed helpers above.
### Source syntax and generic signatures
- An object used as a value now needs `X$.MODULE$` where that companion is retained (event handlers, proxies,
recipes, packet handlers, materials and generators). Static calls alone do not supply a singleton value.
- Object-application sugar becomes explicit `.apply(...)`, e.g. `NormalOcclusionTest.apply(...)` and
| Removed raw `TIInventoryTile$class`; moved implementation into `JInventoryTile` input | `TIInventoryTile` itself remains a Java interface. Source consumers should target it, not the transformed `JInventoryTile` class. |
Inherited/foreign-field and callback workarounds add package-private access shims for redstone, tile-change,
fluid, inventory and both client traits; they are not new consumer APIs. Transformer constraints and shim recipes
are documented once in the handoff.
The client-trait port also deliberately extends the Java-trait transformer:
- Registered Java traits may extend an already registered Java trait; the parent runtime interface participates in
hierarchy and linearization.
- Explicit field accessors are recognized rather than duplicated.
- Transient input fields are excluded from automatic `copyFrom`. Client render caches use this opt-out; their