diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index 9a56ef3..c7f2d5a 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -24,13 +24,13 @@ 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. Three files still contain generated +placement/render helpers, both generators and all built-in tile traits are Java. Two files still contain generated microblock implementations in Scala, alongside the compiler/signature/analyser compatibility shells. The common, face, corner, edge and post traits retain 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 -`PostMicroblockClient` in `microblock/EdgeMicroblockTraits.scala`. Retain the post super-dispatch bridge and the -ScalaSignature path-dependent model bridges. +`HollowMicroblock` in `microblock/HollowMicroblockTraits.scala`, followed separately by its client trait. Retain the +post lifecycle/super-dispatch bridges and 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 @@ -367,7 +367,7 @@ cache remain reference-identical; pass-through-interface coverage remains green. ### Phase 6 — Convert multipart core and microblocks Status: core code, factories, ordinary helpers and common/face/corner/edge/post microblock trait behavior are Java. -Three files still contain generated trait implementations. Extract their behavior while retaining Scala declarations where +Two files still contain generated trait implementations. 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. diff --git a/JAVA_MIGRATION_CONSUMER_AUDIT.md b/JAVA_MIGRATION_CONSUMER_AUDIT.md index ea3c679..cce4d47 100644 --- a/JAVA_MIGRATION_CONSUMER_AUDIT.md +++ b/JAVA_MIGRATION_CONSUMER_AUDIT.md @@ -518,6 +518,8 @@ opaque draws. Its Forge companion pins generated face bounds and solidity. The ` tested by Extra Utilities' connected-texture renderer remains intact. Corner, edge and post tests freeze virtual access, every supported geometry, packed shape/material NBT and descriptions, edge conduction and post occlusion ordering. The post's frozen Scala superclass also pins its synthetic super dispatch, including short-circuiting. +Post-client fixtures additionally exercise render/lifecycle dispatch, equality receivers, repeated virtual reads and +ordered neighbour shrinking; Forge uses real cover/post geometry while client generation and GPU output remain manual. 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 diff --git a/JAVA_MIGRATION_HANDOFF.md b/JAVA_MIGRATION_HANDOFF.md index b74cd4b..5e6f053 100644 --- a/JAVA_MIGRATION_HANDOFF.md +++ b/JAVA_MIGRATION_HANDOFF.md @@ -16,22 +16,22 @@ migration checkout; `codex/tile-compatibility-fixes` was deleted after its fixes ## Current state and next target -**357 plain-JVM tests and 226 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **219 Java -files and 9 Scala files / 1,140 nonblank Scala lines**. The packaged inventory has 442 classes. +**365 plain-JVM tests and 227 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **220 Java +files and 9 Scala files / 1,104 nonblank Scala lines**. The packaged inventory has 443 classes. -Latest bounded target: `PostMicroblock` in `EdgeMicroblockTraits.scala` delegates its implementation to -`PostMicroblockTraitLogic.java`. Its Scala declaration, inheritance metadata, `$class` bridges and singleton accessor -remain. The Scala occlusion bridge retains the synthetic super call after Java evaluates the special cases and box -test. Characterization was committed first as `afd6c69`, with frozen Scala post/face implementors and a real superclass -predecessor. Tests pin call/failure ordering, virtual dispatch, read-only list behavior, all nine supported bounds, -edge-item drops/picking, torch support, generated post/cover occlusion and NBT/description round-trips. Clean -verification after stopping Gradle matches all 441 original class/member APIs, all 17 ScalaSignature payloads, -3,696 non-target method bodies and all 116 generated dump names/hashes. Only the package-private helper is added. -Local evidence is in `run/migration-post-trait-reference/`; `clean` preserves it. +Latest bounded target: `PostMicroblockClient` delegates render/recalculation/shrink behavior to +`PostMicroblockClientLogic.java`. Scala retains its state, inheritance metadata, `$class` and lifecycle/super +forwarders. Characterization was committed first as `47885d1`: frozen Scala forwarders pin render pass selection, +material reuse, repeated getters, equality receivers, collection `foreach`, split/reset behavior and lifecycle +failure ordering. Forge exercises real cover/post geometry on all three axes through the client helper; actual +client generation/GPU output remains a manual gate. Clean verification after stopping Gradle preserves all 441 +retained class/member APIs, all 17 ScalaSignature payloads, 3,703 unrelated method bodies and all 116 generated dumps. +One internal Scala callback is replaced by the Java helper/callback under the shared classfile policy. Local evidence +is in `run/migration-post-client-reference/`; `clean` preserves it. -**Next: port `PostMicroblockClient` in `microblock/EdgeMicroblockTraits.scala`.** Characterize render pass/bounds -dispatch, lifecycle super-call ordering, face/post shrinking, split-bound reset and the size/transparency/axis -tie-breaks before conversion. Retain equality semantics and observe virtual calls rather than caching them. +**Next: port `HollowMicroblock` in `microblock/HollowMicroblockTraits.scala`.** Characterize its center connector +lookup, hollow-size/slot geometry, normal/partial/collision boxes, subparts and occlusion super ordering before +conversion. Leave `HollowMicroblockClient` rendering for a separate bounded target. Retain the Scala declarations where inheritance metadata is required, the external ProjectRed Scala-trait fixture and the ScalaSignature model bridges; compiler changes stay separate. @@ -42,8 +42,7 @@ 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` | Six retained inheritance/bridge shells over Java implementation | -| `microblock/EdgeMicroblockTraits.scala` | Edge/post shells over Java behavior, with retained post super dispatch; `PostMicroblockClient` implementation remains | +| `microblock/MicroblockTraits.scala`, `FaceMicroblockTraits.scala`, `CornerMicroblockTraits.scala`, `EdgeMicroblockTraits.scala` | Nine retained state/inheritance/bridge shells over Java implementation, including post lifecycle/super dispatch | | `microblock/HollowMicroblockTraits.scala`, `TMicroOcclusion.scala` | Generated trait implementations; port in bounded groups while preserving inheritance metadata | Both generators, both registries, core tile/part classes, ordinary microblock helpers/factories, handlers, networking, diff --git a/JAVA_MIGRATION_MANUAL_CHECKS.md b/JAVA_MIGRATION_MANUAL_CHECKS.md index 63abb96..4ee46f2 100644 --- a/JAVA_MIGRATION_MANUAL_CHECKS.md +++ b/JAVA_MIGRATION_MANUAL_CHECKS.md @@ -41,7 +41,7 @@ Lamp Cover**, **Hollow Inverted White Lamp Cover**, **Inverted White Lamp Nook** | [ ] | Render several block micro-materials with Angelica shaders enabled, then render an unrelated block | Each face receives the matching block/meta shader material and the override is reset after the microblock draw | `MaterialRenderHelper.blockAndMeta` / `render` | | [ ] | Inspect face, hollow, edge and corner microblocks in inventory and as dropped entities | Every size/material combination uses the correct localized name and centered shape/material render | `ItemMicroPart` / `ItemMicroPartRenderer` / `MicroblockRender.renderItem` | | [ ] | Saw **Stone**, **Glass** and a ProjectRed **Inverted White Lamp** into face covers; place them on all six faces beside other microblocks, then inspect their inventory renders | Opaque covers retain their outer face while internal faces follow the shrink mask; transparent covers use the shrunk bounds, and inventory renders show the complete shape without missing or doubled faces | `FaceMicroblockClient.render` / `FaceMicroblockTraitLogic.render` | -| [ ] | Place crossing posts of different sizes/transparency beside face covers, then add and remove neighbours | Post segments shrink and split at intersections without missing sections or stale render bounds | `PostMicroblockClient.recalcBounds` / `shrinkFace` / `shrinkPost` | +| [ ] | Saw **Stone**, **Glass** and a ProjectRed **Inverted White Lamp** into strip/post shapes; place crossing posts on all three axes between face covers, then add and remove neighbours | Compare both split segments with the reference, including the second segment's use of physical bounds; removal resets the split without stale geometry or missing sections | `PostMicroblockClient.recalcBounds` / `PostMicroblockClientLogic` / `shrinkFace` / `shrinkPost` | | [ ] | Run connected AE2 **ME Glass Cable**, **ME Covered Cable** and **ME Dense Smart Cable**, plus ProjectRed **Framed Red Alloy Wire**, through hollow stone/glass covers on all six faces | Each opening follows the connector size; opaque and transparent rims, breaking overlay and highlight have no missing, doubled or stale sections after neighbour changes | `HollowMicroblockClient.recalcBounds` / `renderHollow` / `drawBreaking` / `drawHighlight` | | [ ] | Place touching face, corner and edge microblocks of mixed sizes/transparency, then add and remove neighbours | Intersections choose the same winner, opaque covered faces stay masked, and every surviving segment refreshes without gaps or stale bounds | `MicroOcclusion` / `TMicroOcclusionClient.recalcBounds` | diff --git a/docs/migration/HISTORY.md b/docs/migration/HISTORY.md index 3497e5d..092e44d 100644 --- a/docs/migration/HISTORY.md +++ b/docs/migration/HISTORY.md @@ -1141,3 +1141,25 @@ differences belong in the [divergence ledger](../../JAVA_MIGRATION_DIVERGENCES.m versions remain verified. Sources total 219 Java files and 9 Scala files / 1,140 nonblank Scala lines. - Next: `PostMicroblockClient`; characterize render dispatch, lifecycle super ordering, shrink/split/reset behavior and size/transparency/axis tie-breaks before conversion. Client/GPU and full-pack checks remain manual. + +### 2026-09-03 — Post microblock client implementation + +- Committed eight JVM characterization tests and one Forge test first as `47885d1`. Frozen Scala 2.11.5 client forwarders + and lifecycle predecessors pin the exact `-1` render branch, material reuse, live second-segment changes, repeated + virtual size/transparency/axis reads, superclass ordering/exceptions and equality receiver dispatch (even on the + same reference). Tests also pin collection `foreach`, neighbour order, fresh bounds copies, split reuse/reset and + malformed face failures. Forge exercises real cover/post geometry across all three axes through the client helper. +- Extracted five behavior methods to `PostMicroblockClientLogic.java`; retained Scala state/inheritance metadata, + accessors and lifecycle/super forwarders. The named Java callback preserves `foreach` and replaces the internal + Scala anonymous callback under the existing shared classfile policy. No algorithm fix or new effective divergence + was introduced. In particular, the second split segment still copies physical bounds rather than the already + face-clipped first segment; the new Forge check records that reference behavior. +- Saved source/jar, reports and 116 generated dumps in ignored `run/migration-post-client-reference/`. Clean checks + preserve all 441 retained class/member APIs, all 17 ScalaSignature payloads, 3,703 non-target method bodies and all + 116 generated names/hashes. Two Java classes replace one Scala callback, bringing the jar inventory to 443 classes. + Formatting, checkstyle, build and Forge pass normally and after stopping Gradle for a clean rebuild: 365 JVM / + 227 Forge tests, zero failures/errors/skips. The forced Scala-compilation guard and all five packaged `@Mod` versions + remain verified. Sources total 220 Java files and 9 Scala files / 1,104 nonblank Scala lines. +- Expanded the existing manual post check with Stone, Glass and ProjectRed Inverted White Lamp examples. The + dedicated server strips the client factory entry point; helper/geometry coverage does not establish actual client + generation or GPU output. Next: `HollowMicroblock`, followed separately by `HollowMicroblockClient`. diff --git a/src/main/scala/codechicken/microblock/EdgeMicroblockTraits.scala b/src/main/scala/codechicken/microblock/EdgeMicroblockTraits.scala index 0fcbbca..0951212 100644 --- a/src/main/scala/codechicken/microblock/EdgeMicroblockTraits.scala +++ b/src/main/scala/codechicken/microblock/EdgeMicroblockTraits.scala @@ -25,16 +25,8 @@ trait PostMicroblockClient extends PostMicroblock with MicroblockClient { var renderBounds1: Cuboid6 = _ var renderBounds2: Cuboid6 = _ - override def render(pos: Vector3, pass: Int) { - val mat = getIMaterial - if (pass == -1) - MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0) - else { - MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0) - if (renderBounds2 != null) - MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0) - } - } + override def render(pos: Vector3, pass: Int) = + PostMicroblockClientLogic.render(this, pos, pass) override def onPartChanged(part: TMultiPart) { recalcBounds() @@ -50,46 +42,15 @@ trait PostMicroblockClient extends PostMicroblock with MicroblockClient { recalcBounds() } - def recalcBounds() { - renderBounds1 = getBounds.copy - renderBounds2 = null + def recalcBounds() = PostMicroblockClientLogic.recalcBounds(this) - shrinkFace(getShape << 1) - shrinkFace(getShape << 1 | 1) + def shrinkFace(fside: Int) = PostMicroblockClientLogic.shrinkFace(this, fside) - tile.partList.foreach(p => - if (p.isInstanceOf[PostMicroblock] && p != this) - shrinkPost(p.asInstanceOf[PostMicroblock]) - ) - } + def shrinkPost(post: PostMicroblock) = + PostMicroblockClientLogic.shrinkPost(this, post) - def shrinkFace(fside: Int) { - val part = tile.partMap(fside) - if (part != null && part.getType.equals("mcr_face")) - MicroOcclusion.shrink( - renderBounds1, - part.asInstanceOf[CommonMicroblock].getBounds, - fside - ) - } - - def shrinkPost(post: PostMicroblock) { - if (post == this) - return - - if (thisShrinks(post)) { - if (renderBounds2 == null) - renderBounds2 = getBounds.copy - MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape << 1 | 1) - MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape << 1) - } - } - - def thisShrinks(other: PostMicroblock): Boolean = { - if (getSize != other.getSize) return getSize < other.getSize - if (isTransparent != other.isTransparent) return isTransparent - return getShape > other.getShape - } + def thisShrinks(other: PostMicroblock): Boolean = + PostMicroblockClientLogic.thisShrinks(this, other) } trait PostMicroblock diff --git a/src/main/scala/codechicken/microblock/PostMicroblockClientLogic.java b/src/main/scala/codechicken/microblock/PostMicroblockClientLogic.java new file mode 100644 index 0000000..46cccde --- /dev/null +++ b/src/main/scala/codechicken/microblock/PostMicroblockClientLogic.java @@ -0,0 +1,83 @@ +package codechicken.microblock; + +import codechicken.lib.vec.Vector3; +import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial; +import codechicken.multipart.TMultiPart; +import scala.runtime.AbstractFunction1; +import scala.runtime.BoxedUnit; + +/** Client behavior behind the retained Scala state, inheritance and lifecycle bridges. */ +final class PostMicroblockClientLogic { + + private PostMicroblockClientLogic() {} + + static void render(PostMicroblockClient part, Vector3 pos, int pass) { + IMicroMaterial material = ((Microblock) part).getIMaterial(); + if (pass == -1) { + MicroblockRender.renderCuboid(pos, material, pass, part.getBounds(), 0); + } else { + MicroblockRender.renderCuboid(pos, material, pass, part.renderBounds1(), 0); + if (part.renderBounds2() != null) { + MicroblockRender.renderCuboid(pos, material, pass, part.renderBounds2(), 0); + } + } + } + + static void recalcBounds(PostMicroblockClient part) { + part.renderBounds1_$eq(part.getBounds().copy()); + part.renderBounds2_$eq(null); + part.shrinkFace(((Microblock) part).getShape() << 1); + part.shrinkFace(((Microblock) part).getShape() << 1 | 1); + ((TMultiPart) part).tile().partList().foreach(new ShrinkPosts(part)); + } + + static void shrinkFace(PostMicroblockClient part, int side) { + TMultiPart other = ((TMultiPart) part).tile().partMap(side); + if (other != null && other.getType().equals("mcr_face")) { + MicroOcclusion.shrink(part.renderBounds1(), ((CommonMicroblock) other).getBounds(), side); + } + } + + static void shrinkPost(PostMicroblockClient part, PostMicroblock other) { + // Scala equality calls the left operand's equals even when the references are identical. + if (other == null ? part == null : other.equals(part)) { + return; + } + if (part.thisShrinks(other)) { + if (part.renderBounds2() == null) { + part.renderBounds2_$eq(part.getBounds().copy()); + } + MicroOcclusion.shrink(part.renderBounds1(), other.getBounds(), ((Microblock) part).getShape() << 1 | 1); + MicroOcclusion.shrink(part.renderBounds2(), other.getBounds(), ((Microblock) part).getShape() << 1); + } + } + + static boolean thisShrinks(PostMicroblockClient part, PostMicroblock other) { + if (((Microblock) part).getSize() != ((Microblock) other).getSize()) { + return ((Microblock) part).getSize() < ((Microblock) other).getSize(); + } + if (((Microblock) part).isTransparent() != ((Microblock) other).isTransparent()) { + return ((Microblock) part).isTransparent(); + } + return ((Microblock) part).getShape() > ((Microblock) other).getShape(); + } + + private static final class ShrinkPosts extends AbstractFunction1 + implements scala.Serializable { + + private static final long serialVersionUID = 0L; + private final PostMicroblockClient part; + + ShrinkPosts(PostMicroblockClient part) { + this.part = part; + } + + @Override + public BoxedUnit apply(TMultiPart other) { + if (other instanceof PostMicroblock && !other.equals(part)) { + part.shrinkPost((PostMicroblock) other); + } + return BoxedUnit.UNIT; + } + } +}