From d89aaeacf360c3d51340bc34dfae5c967a985fee Mon Sep 17 00:00:00 2001 From: Algent Date: Thu, 3 Sep 2026 21:14:54 +0200 Subject: [PATCH] refactor: port common microblock logic to Java Retain Scala trait declarations and helper bridges for inherited mixin registration and existing consumers. --- JAVA_MIGRATION.md | 26 ++++--- JAVA_MIGRATION_CONSUMER_AUDIT.md | 9 ++- JAVA_MIGRATION_HANDOFF.md | 40 ++++++----- docs/migration/HISTORY.md | 23 +++++++ .../microblock/MicroblockTraitLogic.java | 68 +++++++++++++++++++ .../microblock/MicroblockTraits.scala | 42 ++++-------- 6 files changed, 148 insertions(+), 60 deletions(-) create mode 100644 src/main/scala/codechicken/microblock/MicroblockTraitLogic.java diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index f0a49f5..bd720ea 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -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. diff --git a/JAVA_MIGRATION_CONSUMER_AUDIT.md b/JAVA_MIGRATION_CONSUMER_AUDIT.md index 5f3c0ec..aa4cc1c 100644 --- a/JAVA_MIGRATION_CONSUMER_AUDIT.md +++ b/JAVA_MIGRATION_CONSUMER_AUDIT.md @@ -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. diff --git a/JAVA_MIGRATION_HANDOFF.md b/JAVA_MIGRATION_HANDOFF.md index 2e6696f..5d4101f 100644 --- a/JAVA_MIGRATION_HANDOFF.md +++ b/JAVA_MIGRATION_HANDOFF.md @@ -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 diff --git a/docs/migration/HISTORY.md b/docs/migration/HISTORY.md index d009fb0..7fc9060 100644 --- a/docs/migration/HISTORY.md +++ b/docs/migration/HISTORY.md @@ -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. diff --git a/src/main/scala/codechicken/microblock/MicroblockTraitLogic.java b/src/main/scala/codechicken/microblock/MicroblockTraitLogic.java new file mode 100644 index 0000000..b82d809 --- /dev/null +++ b/src/main/scala/codechicken/microblock/MicroblockTraitLogic.java @@ -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 getPartialOcclusionBoxes(CommonMicroblock part) { + return JavaConversions.seqAsJavaList( + (Seq) Seq$.MODULE$.apply(Predef$.MODULE$.wrapRefArray(new Cuboid6[] { part.getBounds() }))); + } + + static int itemClassID(CommonMicroblock part) { + return part.microClass().getClassId(); + } +} diff --git a/src/main/scala/codechicken/microblock/MicroblockTraits.scala b/src/main/scala/codechicken/microblock/MicroblockTraits.scala index a1614af..fd03528 100644 --- a/src/main/scala/codechicken/microblock/MicroblockTraits.scala +++ b/src/main/scala/codechicken/microblock/MicroblockTraits.scala @@ -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) }