refactor: port common microblock logic to Java

Retain Scala trait declarations and helper bridges for inherited mixin registration and existing consumers.
This commit is contained in:
Algent committed 2026-09-03 21:14:54 +02:00
1 parent d6eb56b56f
commit d89aaeacf3
6 files changed
+148 -60

No files matched your search

+15 -11
View File
@@ -24,11 +24,12 @@ No trustworthy tool will produce a maintainable Java port automatically. A decom
## Current status
Work continues on `algent/java`. The low-coupling queue, core part/tile types, registries, handlers, factories,
placement/render helpers, both generators and all built-in tile traits are Java. Six generated microblock trait
files and the compiler/signature/analyser compatibility shells remain Scala. Composite generation, Java-trait
rewriting and compiler startup now delegate to Java helpers. Abstract Java mixins and Java-path side-only filtering
are complete; the next bounded target is `microblock/MicroblockTraits.scala`. Retain the ScalaSignature
path-dependent model bridges.
placement/render helpers, both generators and all built-in tile traits are Java. Five generated microblock trait
implementations and the compiler/signature/analyser compatibility shells remain Scala. `MicroblockTraits.scala`
retains three inheritance/bridge declarations over Java behavior. Composite generation, Java-trait rewriting and
compiler startup also delegate to Java helpers. Abstract Java mixins and Java-path side-only filtering are complete;
multiple Scala-trait inheritance still needs its metadata. The next bounded target is the implementation in
`microblock/FaceMicroblockTraits.scala`. Retain the ScalaSignature path-dependent model bridges.
Start with [JAVA_MIGRATION_HANDOFF.md](JAVA_MIGRATION_HANDOFF.md) for the exact source/test baseline, workflow and
Java-source limitations. Read both the [ABI inventory](JAVA_MIGRATION_ABI_INVENTORY.md) and
@@ -364,9 +365,10 @@ cache remain reference-identical; pass-through-interface coverage remains green.
### Phase 6 — Convert multipart core and microblocks
Status: core code, factories and ordinary helpers are Java. The six remaining generated microblock trait files
can now move through the Java mixin path because both Phase 7 prerequisites are complete. The following gates
continue to apply to those remaining units; completed per-type evidence is in the history.
Status: core code, factories, ordinary helpers and common microblock trait behavior are Java. Five generated trait
implementations remain. Extract their behavior while retaining Scala declarations where multiple-trait inheritance
still needs signature metadata; abstract Java mixins and side filtering alone do not replace that metadata. The
following gates continue to apply to those remaining units; completed per-type evidence is in the history.
- [ ] Add characterization coverage for each subsystem immediately before its conversion.
- [ ] Convert the central tile, part, registry, placement, rendering, networking, scheduler, and microblock code in dependency order.
@@ -548,8 +550,10 @@ The rewrite has two Phase 7 prerequisites:
`@SideOnly(Side.CLIENT)` when ProjectRed rewrites the trait to Java, even though its `addTraits` ignores the
`client` argument.
Both compiler prerequisites are complete. The FMP microblock trait files can now move through the Java path; removing
external Scala-signature support still waits for a released ProjectRed Java rewrite.
Both compiler prerequisites for the single-parent ProjectRed rewrite are complete. FMP's own microblock traits also
use multiple Scala-trait inheritance: their implementation can move to Java helpers, while removing their Scala
declarations needs a separate inheritance strategy. Removing external Scala-signature support still waits for a
released ProjectRed Java rewrite.
UtilitiesInExcess is the cheapest case in the table because it is not yet in the pack. Anything fixed before it ships
never becomes a compatibility obligation at all.
@@ -677,7 +681,7 @@ Still open, and only to be decided when they become necessary:
Track remaining work by completed compatibility gates, not the retired pre-audit calendar estimates:
1. Extract the remaining compiler algorithms with unchanged characterization and generated output.
2. Characterize and port the remaining generated microblock traits through the completed Java-mixin path.
2. Characterize and port the remaining generated microblock behavior, retaining necessary trait inheritance metadata.
3. Resolve or explicitly retain the Scala model bridges; finish the applicable API and pre-merge cleanup gates.
4. Complete packaged-pack, client and performance validation with recorded results.
+6 -3
View File
@@ -507,9 +507,12 @@ name like ProjectRed's `LightMicroblock`, direct-Java-source `TPartialOcclusionT
direct-Java-source `TRedstoneTile`'s exact eight-method interface and class caching, and server-only pass-through
forwarding, overloading, single-implementor selection, copying/rebinding, and removal cleanup.
`TRedstoneTileFunctionalTest` additionally pins the ProjectRed/Extra Utilities query surface, masks, conduction, and
world-side routing. `JavaTraitRegistrationFunctionalTest` now pins current/opposite-side selection for visible and
invisible Java annotations, including method dispatch, field state and constructor initialization. Full downstream
mixed tiles and client-side selection remain Phase 7 integration cases.
world-side routing. `JavaTraitRegistrationFunctionalTest` pins current/opposite-side selection for visible and
invisible Java annotations, including method dispatch, field state and constructor initialization.
`MicroblockTraitsCharacterizationTest` executes a frozen concrete Scala consumer through all three common microblock
helper bridges, covering slots, partial-box list behavior, material/render routing and particles. Its Forge companion
covers generated common microblocks; the runtime interfaces used by Extra Utilities, ForgeRelocationFMP and GuideNH
remain intact. Full downstream mixed tiles and client-side selection remain Phase 7 integration cases.
With these gates, the source audit is actionable: the port can simplify internals freely where no listed observation
changes, while each externally observed behavior has a named consumer and a focused way to prove compatibility.
+24 -16
View File
@@ -16,23 +16,23 @@ migration checkout; `codex/tile-compatibility-fixes` was deleted after its fixes
## Current state and next target
**333 plain-JVM tests and 211 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **214 Java
files and 9 Scala files / 1,188 nonblank Scala lines**. The packaged inventory has 437 classes.
**340 plain-JVM tests and 213 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **215 Java
files and 9 Scala files / 1,170 nonblank Scala lines**. The packaged inventory has 438 classes.
Latest bounded compiler change: Java mixins now filter opposite-side fields, methods and constructors before metadata
or bytecode rewriting. The Forge test first characterized the previous retention of visible/invisible annotations,
state and constructor initialization (`a481859`). Current-side members remain; when the no-argument constructor is absent,
an empty `$init$` satisfies the generated initializer contract without executing the opposite-side body. A regression
also passes input through Forge's real side transformer before registration. Clean verification after stopping Gradle
matched 432 class APIs, all five compiler closures and 3,643 non-target method bodies. All 112 reference dump names
remain; 109 hashes are exact and only the trait, helper and composite from the SideOnly fixture differ. Four outputs
are new for the already-stripped-constructor fixture. Local evidence is in
`run/migration-java-side-only-reference/`; `clean` preserves this ignored directory.
Latest bounded target: `MicroblockTraits.scala` delegates its eight remaining implementation methods to
`MicroblockTraitLogic.java`; its particle callbacks already delegated to Java. The three Scala trait declarations,
inheritance metadata and `$class` bridges remain. Characterization was committed first as `d6eb56b`, including a
frozen concrete Scala consumer compiled against the unchanged jar. It exercises initialization, slots, immutable
partial-box views, virtual class/material/render dispatch and particle evaluation order. Forge covers generated common
microblocks and the existing server-side failure when a client fallback reaches the stripped `Block.getIcon`.
Clean verification after stopping Gradle matches all 437 original class/member APIs, all 17 ScalaSignature payloads,
3,674 non-target method bodies and all 116 generated dump names/hashes. Only the package-private Java helper is added.
Local evidence is in `run/migration-microblock-traits-reference/`; `clean` preserves this ignored directory.
**Next: port `microblock/MicroblockTraits.scala` as the first generated microblock trait group.** Both Java-mixin
prerequisites are complete. Characterize its interface/helper ABI, initialization, generated common/client dispatch,
render/effect routing, slots, partial-occlusion boxes and item class ID before conversion. Keep the external ProjectRed
Scala-trait fixture green and retain the ScalaSignature path-dependent model bridges.
**Next: port the implementation in `microblock/FaceMicroblockTraits.scala`.** Characterize bounds/shape indexing,
material solidity, negative-pass rendering, transparent/opaque face masks and generated dispatch before conversion.
Retain the Scala declarations where inheritance metadata is still required. Keep the external ProjectRed Scala-trait
fixture green and retain the ScalaSignature path-dependent model bridges; compiler changes stay separate.
Remaining Scala units:
@@ -41,7 +41,8 @@ Remaining Scala units:
| `multipart/asm/ASMMixinCompiler.scala` | Retained nested models, construction callbacks and Scala entry-point shell |
| `multipart/asm/ScalaSignature.scala` | Named models, primitive/erased bridges and five generic inner-construction branches |
| `multipart/asm/StackAnalyser.scala` | Class/companion/model shell over Java `StackAnalyserLogic` |
| `microblock/MicroblockTraits.scala`, `FaceMicroblockTraits.scala`, `CornerMicroblockTraits.scala`, `EdgeMicroblockTraits.scala`, `HollowMicroblockTraits.scala`, `TMicroOcclusion.scala` | Generated traits; port in dependency order now that both Java-mixin prerequisites are complete |
| `microblock/MicroblockTraits.scala` | Three retained inheritance/bridge shells over Java implementation |
| `microblock/FaceMicroblockTraits.scala`, `CornerMicroblockTraits.scala`, `EdgeMicroblockTraits.scala`, `HollowMicroblockTraits.scala`, `TMicroOcclusion.scala` | Generated trait implementations; port in dependency order while preserving inheritance metadata |
Both generators, both registries, core tile/part classes, ordinary microblock helpers/factories, handlers, networking,
placement/render helpers and built-in tile traits are Java. The low-risk queue and immediate consumer gate are
@@ -102,6 +103,13 @@ Compilation against the migrated artifact would defeat the binary-compatibility
## Retained compiler constraints
- **Microblock trait inheritance still needs Scala metadata.** Abstract Java mixins and Java `@SideOnly` filtering
are supported, but that does not supply Scala multiple-trait linearization. `getAndRegisterParentTraits` collects
Scala-signature traits, while Java registration only incorporates an already registered superclass trait, not
implemented Scala interfaces. `CommonMicroblockClient` combines three trait parents, including the still-Scala
`TMicroOcclusionClient`. Keep its declarations and bridges while extracting behavior; replacing them outright needs
a separately characterized compiler/inheritance strategy. Extra Utilities, ForgeRelocationFMP and GuideNH also use
these runtime interface types.
- **External Scala traits remain supported.** ProjectRed registers `LightMicroblock` through
`MicroblockGenerator.registerTrait`; keep its ScalaSignature ingestion, `$class` helper and generated dispatch.
The Java path now supports its `@SideOnly` requirement, but retiring this external Scala path still needs a released
+23
View File
@@ -1035,3 +1035,26 @@ differences belong in the [divergence ledger](../../JAVA_MIGRATION_DIVERGENCES.m
zero failures/errors/skips. All five packaged `@Mod` versions, the forced
Scala-compilation guard and dev config remain correct. Sources stay at 214 Java files and 9 Scala files / 1,188
nonblank Scala lines. Next: `microblock/MicroblockTraits.scala`, characterized before conversion.
### 2026-09-03 — Common microblock trait implementation
- Committed seven JVM and two Forge characterization tests first as `d6eb56b`. A frozen Scala 2.11.5 concrete
`CommonMicroblockClient`, compiled under Java 8 against the unchanged jar, exercises all three trait helper bridges.
Tests pin initialization, signed shape/slot handling and virtual shift operands, fresh immutable partial-box lists
with shared bounds, dynamic class/material lookup, render-pass gating, common cuboid face masks and particle callback
evaluation order. Forge verifies generated common parts and the original `NoSuchMethodError` when the missing-material
client icon fallback reaches server-stripped `Block.getIcon`; the test does not turn that existing limitation into a fix.
- Moved eight implementation methods to package-private `MicroblockTraitLogic.java`. Particle callbacks were already
Java delegates. Retained the three Scala trait declarations, exact inheritance/signature metadata and `$class`
bridges: Scala parent collection does not recognize Java interfaces as Scala traits, and Java registration does not
incorporate implemented Scala interfaces into linearization. Both prior Java-mixin prerequisites remain useful,
but are insufficient to remove this multiple-inheritance shell without another compiler behavior target.
- Saved the reference jar, source, reports and generated dumps under ignored `run/migration-microblock-traits-reference/`.
The clean comparison preserves all 437 original class/member APIs, all 17 ScalaSignature payloads and 3,674 non-target
method bodies; only the new helper raises the class inventory to 438. All 116 generated dump names/hashes are exact,
including external Scala and pass-through fixtures. No new effective divergence is introduced.
- Formatting, checkstyle, build and Forge pass both normally and after stopping Gradle for a clean rebuild:
340 JVM / 213 Forge tests, zero failures/errors/skips. The forced Scala-compilation guard and all five packaged
`@Mod` versions remain verified. Sources total 215 Java files and 9 Scala files / 1,170 nonblank Scala lines.
Next: `microblock/FaceMicroblockTraits.scala` implementation, with characterization before conversion. GPU rendering
and full client selection remain on the manual checklist.
@@ -0,0 +1,68 @@
package codechicken.microblock;
import java.util.List;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial;
import scala.Predef$;
import scala.collection.JavaConversions;
import scala.collection.Seq;
import scala.collection.Seq$;
/** Implementation behind the Scala trait declarations needed by inherited and external mixins. */
final class MicroblockTraitLogic {
private MicroblockTraitLogic() {}
static IIcon getBrokenIcon(MicroblockClient part, int side) {
IMicroMaterial material = ((Microblock) part).getIMaterial();
return material == null ? Blocks.stone.getIcon(0, 0) : material.getBreakingIcon(side);
}
static boolean renderStatic(MicroblockClient part, Vector3 pos, int pass) {
if (((Microblock) part).getIMaterial().canRenderInPass(pass)) {
part.render(pos, pass);
return true;
}
return false;
}
static Cuboid6 getRenderBounds(MicroblockClient part) {
return part.getBounds();
}
static void render(CommonMicroblockClient part, Vector3 pos, int pass) {
if (pass < 0) {
MicroblockRender.renderCuboid(pos, ((Microblock) part).getIMaterial(), pass, part.getBounds(), 0);
} else {
MicroblockRender.renderCuboid(
pos,
((Microblock) part).getIMaterial(),
pass,
part.renderBounds(),
part.renderMask());
}
}
static int getSlot(CommonMicroblock part) {
return ((Microblock) part).getShape();
}
static int getSlotMask(CommonMicroblock part) {
return 1 << part.getSlot();
}
@SuppressWarnings("unchecked")
static List<Cuboid6> getPartialOcclusionBoxes(CommonMicroblock part) {
return JavaConversions.seqAsJavaList(
(Seq<Cuboid6>) Seq$.MODULE$.apply(Predef$.MODULE$.wrapRefArray(new Cuboid6[] { part.getBounds() })));
}
static int itemClassID(CommonMicroblock part) {
return part.microClass().getClassId();
}
}
@@ -8,18 +8,14 @@ import codechicken.multipart.{
TSlottedPart
}
import net.minecraft.client.particle.EffectRenderer
import net.minecraft.init.Blocks
import net.minecraft.util.MovingObjectPosition
import scala.collection.JavaConversions._
// Keep Scala inheritance metadata and helper bridges while derived and external mixins use Scala registration.
trait MicroblockClient
extends Microblock
with TIconHitEffects
with IMicroMaterialRender {
def getBrokenIcon(side: Int) = getIMaterial match {
case null => Blocks.stone.getIcon(0, 0)
case mat => mat.getBreakingIcon(side)
}
def getBrokenIcon(side: Int) = MicroblockTraitLogic.getBrokenIcon(this, side)
// TIconHitEffects is a Java interface, so these must be declared explicitly or TMultiPart's empty versions win.
override def addHitEffects(
@@ -30,35 +26,20 @@ trait MicroblockClient
override def addDestroyEffects(effectRenderer: EffectRenderer) =
IconHitEffects.addDestroyEffects(this, effectRenderer)
override def renderStatic(pos: Vector3, pass: Int) = {
if (getIMaterial.canRenderInPass(pass)) {
render(pos, pass)
true
} else
false
}
override def renderStatic(pos: Vector3, pass: Int) =
MicroblockTraitLogic.renderStatic(this, pos, pass)
def render(pos: Vector3, pass: Int)
override def getRenderBounds = getBounds
override def getRenderBounds = MicroblockTraitLogic.getRenderBounds(this)
}
trait CommonMicroblockClient
extends CommonMicroblock
with MicroblockClient
with TMicroOcclusionClient {
def render(pos: Vector3, pass: Int) {
if (pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else
MicroblockRender.renderCuboid(
pos,
getIMaterial,
pass,
renderBounds,
renderMask
)
}
def render(pos: Vector3, pass: Int) =
MicroblockTraitLogic.render(this, pos, pass)
}
trait CommonMicroblock
@@ -68,9 +49,10 @@ trait CommonMicroblock
with TSlottedPart {
def microClass: CommonMicroClass
def getSlot = getShape
def getSlotMask = 1 << getSlot
def getPartialOcclusionBoxes = Seq(getBounds)
def getSlot = MicroblockTraitLogic.getSlot(this)
def getSlotMask = MicroblockTraitLogic.getSlotMask(this)
def getPartialOcclusionBoxes =
MicroblockTraitLogic.getPartialOcclusionBoxes(this)
override def itemClassID = microClass.getClassId
override def itemClassID = MicroblockTraitLogic.itemClassID(this)
}