From 9db07f8c7cacefff9ef2f70840bbe6594a48739f Mon Sep 17 00:00:00 2001 From: Algent Date: Sat, 15 Aug 2026 15:32:45 +0200 Subject: [PATCH] refactor: port the marker interfaces to Java TSlottedPart, IRandomDisplayTick, INeighborTileChange, TRandomUpdateTick, ISidedHollowConnect and IMicroMaterialRender. All six are member- and descriptor-identical to the reference. The seven implementors and mixin tiles were diffed too and are unchanged member for member. Five were already pure abstract interfaces in bytecode, so their conversion has no bytecode consequence; exactly one class leaves the jar, TRandomUpdateTick$class. onWorldJoin cannot become a default: TMultiPart declares it, and a superclass method beats an interface default, so the auto-registration would silently never run. It stays abstract and implementors call TickScheduler.loadRandomTick themselves, as RedstoneTorchPart already did. No bridge was written for TRandomUpdateTick$class. No jar in the pack references TRandomUpdateTick in any form, and the frozen baseline holds exactly the eight helpers the inventory lists. 99 plain-JVM tests and 5 Forge server tests pass. --- JAVA_MIGRATION.md | 11 ++++ JAVA_MIGRATION_DIVERGENCES.md | 51 +++++++++++++++++++ JAVA_MIGRATION_HANDOFF.md | 21 +++++--- .../microblock/IMicroMaterialRender.java | 26 ++++++++++ .../microblock/IMicroMaterialRender.scala | 21 -------- .../microblock/ISidedHollowConnect.java | 14 +++++ .../microblock/ISidedHollowConnect.scala | 15 ------ .../multipart/INeighborTileChange.java | 14 +++++ .../multipart/INeighborTileChange.scala | 16 ------ .../multipart/IRandomDisplayTick.java | 14 +++++ .../multipart/IRandomDisplayTick.scala | 14 ----- .../multipart/TRandomUpdateTick.java | 30 +++++++++++ .../multipart/TRandomUpdateTick.scala | 19 ------- .../codechicken/multipart/TSlottedPart.java | 16 ++++++ .../codechicken/multipart/TSlottedPart.scala | 14 ----- 15 files changed, 189 insertions(+), 107 deletions(-) create mode 100644 src/main/scala/codechicken/microblock/IMicroMaterialRender.java delete mode 100644 src/main/scala/codechicken/microblock/IMicroMaterialRender.scala create mode 100644 src/main/scala/codechicken/microblock/ISidedHollowConnect.java delete mode 100644 src/main/scala/codechicken/microblock/ISidedHollowConnect.scala create mode 100644 src/main/scala/codechicken/multipart/INeighborTileChange.java delete mode 100644 src/main/scala/codechicken/multipart/INeighborTileChange.scala create mode 100644 src/main/scala/codechicken/multipart/IRandomDisplayTick.java delete mode 100644 src/main/scala/codechicken/multipart/IRandomDisplayTick.scala create mode 100644 src/main/scala/codechicken/multipart/TRandomUpdateTick.java delete mode 100644 src/main/scala/codechicken/multipart/TRandomUpdateTick.scala create mode 100644 src/main/scala/codechicken/multipart/TSlottedPart.java delete mode 100644 src/main/scala/codechicken/multipart/TSlottedPart.scala diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index 5b0517c..c8ce789 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -498,3 +498,14 @@ These ranges were formed before designing the characterization suite and should - Generalised the default-versus-abstract rule. `TItemMultiPart` extends `Item` rather than `TMultiPart`, and the same split applied: `getHitDepth` became a default because `Item` does not declare it, while `onItemUse` stayed abstract because `Item` does. What matters is the implementor's actual superclass, not `TMultiPart` specifically. - Confirmed that placement logic is testable headless. `onItemUse` short-circuits as soon as `newPart` returns null, before any world, player or stack access, so the depth branch, the neighbour offset and the shared mutated `BlockCoord` can all be asserted with null arguments. - Added the first functional coverage of runtime class generation interacting with a ported type. Microblock classes are built by ASM at run time, so a plain-JVM test cannot show that a generated class resolves a Java interface default; the Forge server suite now generates a `FaceMicroblock` and asserts it inherits `redstoneConductionMap`, and generates a `HollowMicroblock` and asserts its own `0x10` and `false` still win. + +### 2026-08-15 + +- Ported the six marker interfaces: `TSlottedPart`, `IRandomDisplayTick`, `INeighborTileChange`, `TRandomUpdateTick`, `ISidedHollowConnect` and `IMicroMaterialRender`. All six are member- and descriptor-identical to the reference, and the seven implementors and mixin tiles were diffed as well and are unchanged member for member. +- Confirmed that five of the six were already pure abstract interfaces in bytecode with no `$class` helper, so their conversion had no bytecode consequence at all. Exactly one class left the jar: `TRandomUpdateTick$class`. +- Applied the default-versus-abstract rule to `TRandomUpdateTick.onWorldJoin` and got the failing answer: `TMultiPart` declares `onWorldJoin`, so a default would be shadowed by the superclass and the auto-registration would silently never run. It stays abstract, and implementors declare it and call `TickScheduler.loadRandomTick` themselves, which `RedstoneTorchPart` — the only implementor in this codebase — already did. +- Applied the bridge rule to `TRandomUpdateTick$class` and got a clean negative: no jar in the pack references `TRandomUpdateTick` in any form, and the frozen baseline contains exactly the eight `$class` helpers the inventory lists. No bridge was written, following the `IDWriter` precedent. The accepted cost is a recompiled-Scala-consumer break with no shipping consumer. +- Found that `IMicroMaterialRender` is implemented for every part solely by `TMultiPart`'s Scala-style `world`/`x`/`y`/`z`/`getRenderBounds` accessors. Renaming any of them to a bean accessor would silently unimplement the interface, so the names are now pinned by a characterization test rather than left to reviewer attention. +- Established that for interfaces carrying no implementation, the characterization is the shape: every member abstract and public, no superinterface, and the exact member set. That is enough to catch the two realistic failure modes, a stray default and a renamed accessor. +- Noted that `IRedstonePart.scala` is misleadingly named and is not a marker-trait file. It carries six traits plus `RedstoneInteractions`, whose `MODULE$` is load-bearing, and was moved out of the low-risk group. +- Noted a documentation gap, not addressed here: this log was last appended for the `TItemMultiPart` port. The eight ports between it and this entry — `TEdgePart`, `Saw`, both registries, `TileMultipart`, `TMultiPart`, `TickScheduler` and `BlockMultipart` — are recorded in `JAVA_MIGRATION_DIVERGENCES.md` but never made it here, so the "Current status" section above should be read with that in mind. diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index 031ec15..c0e0318 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -846,3 +846,54 @@ nested inner closures, are removed. The Java loops produce no replacement classe - Almost everything else in this class needs a player, a ray trace or a renderer. Block breaking, selection boxes, collision, pick block, activation, particles and light are all on the manual checklist and are the reason that checklist exists. + +## 2026-08-15 — Marker interface Java port + +Six interfaces that carry no implementation: `TSlottedPart`, `IRandomDisplayTick`, `INeighborTileChange`, +`TRandomUpdateTick`, `ISidedHollowConnect` and `IMicroMaterialRender`. + +### Observable behavior + +No known divergence. Five of the six were already pure abstract interfaces in bytecode with no `$class` helper, so +their conversion is a source-language change with no bytecode consequence at all. The sixth is discussed below. + +### Supported JVM API + +All six are member-identical and descriptor-identical to the reference. Every implementor and mixin was diffed as +well — `Microblock`, `RedstoneTorchPart`, `TorchPart`, `HollowMicroblock`, `TSlottedTile`, `TTileChangeTile` and +`TRandomDisplayTickTile` are unchanged member for member, so no concrete class gained or lost a forwarder. + +`TSlottedPart` and `TRandomUpdateTick` extended `TMultiPart` in Scala source. A trait extending a class is a bare +interface in bytecode, carrying none of the class's members, so the Java interfaces are bare too and match the +reference exactly. This is the same pattern as `JIconHitEffects` and `TileMultipartClient`; the port makes it explicit +in the source rather than changing it. + +`IMicroMaterialRender` keeps the Scala accessor names `world`, `x`, `y` and `z`. `TMultiPart` declares all five +members under exactly these names, which is what implements the interface for every part. Renaming any of them to a +bean accessor would silently unimplement it, so the names are pinned by a characterization test. + +### Removed API + +`TRandomUpdateTick$class`, with its `onWorldJoin` and `$init$` statics. It is the only class removed from the jar by +this port; the full class list is otherwise identical. + +`onWorldJoin` **cannot** become an interface default. `TMultiPart` declares `onWorldJoin`, and a superclass method +always beats an interface default on the JVM, so the default would never run and the failure would be silent. It stays +abstract, and each implementor declares it and calls `TickScheduler.loadRandomTick` itself, which is what +`RedstoneTorchPart` — the only implementor in this codebase — already did. The Scaladoc already carried this warning +for Java implementors; it now applies to Scala implementors too. + +No jar in the pack references `TRandomUpdateTick` in any form, and the frozen baseline contains exactly the eight +`$class` helpers listed in the inventory, which does not include this one. Per the `IDWriter` precedent, no +compatibility bridge was written. The consequence to accept: a Scala class mixing in `TRandomUpdateTick` and relying +on the trait's `onWorldJoin` must now declare it, or it will resolve to `TMultiPart`'s no-op and never register for +random ticks. This is a recompiled-Scala-consumer break with no shipping consumer. + +### Validation + +- Complete plain-JVM suite: 99 tests, 0 failures, 0 errors. +- Clean `spotlessApply checkstyleTest build`: passing. +- Java 8 Forge dedicated-server suite: 5 tests, 0 failures, 0 errors. +- Public members, descriptors and the full emitted class list diffed against a reference jar built at `3e68e67`. +- Random tick firing over time still needs a world and stays on the manual checklist, as does torch display-tick + rendering and hollow-cover connection sizing. diff --git a/JAVA_MIGRATION_HANDOFF.md b/JAVA_MIGRATION_HANDOFF.md index 9a685d5..3efb153 100644 --- a/JAVA_MIGRATION_HANDOFF.md +++ b/JAVA_MIGRATION_HANDOFF.md @@ -10,7 +10,7 @@ what breaks, and what is left. The other documents hold the reasoning. | `JAVA_MIGRATION_DIVERGENCES.md` | Every intentional difference from the reference, one entry per port | | `JAVA_MIGRATION_MANUAL_CHECKS.md` | What no automated test can cover, and must be checked by hand in a client | -Branch: `algent/java`. Base: `master`. 39 commits so far. +Branch: `algent/java`. Base: `master`. 41 commits so far. ## The one rule that matters @@ -56,7 +56,7 @@ awk -F'"' '/ + * The accessors keep their Scala names because TMultiPart already declares world/x/y/z/getRenderBounds under exactly + * these names, which is what implements this interface for every part. + */ +public interface IMicroMaterialRender { + + /** May be null for inventory rendering. */ + World world(); + + int x(); + + int y(); + + int z(); + + /** Return the bounds of the part for texture mapping side decals like grass. */ + Cuboid6 getRenderBounds(); +} diff --git a/src/main/scala/codechicken/microblock/IMicroMaterialRender.scala b/src/main/scala/codechicken/microblock/IMicroMaterialRender.scala deleted file mode 100644 index eee9b36..0000000 --- a/src/main/scala/codechicken/microblock/IMicroMaterialRender.scala +++ /dev/null @@ -1,21 +0,0 @@ -package codechicken.microblock - -import codechicken.lib.vec.Cuboid6 -import net.minecraft.world.World - -/** Wrapper class blocks/tiles/parts wanting to use the micro material system to - * render part of their models. - */ -trait IMicroMaterialRender { - - /** May be null for inventory rendering. - */ - def world: World - def x: Int - def y: Int - def z: Int - - /** Return the bounds of the part for texture mapping side decals like grass - */ - def getRenderBounds: Cuboid6 -} diff --git a/src/main/scala/codechicken/microblock/ISidedHollowConnect.java b/src/main/scala/codechicken/microblock/ISidedHollowConnect.java new file mode 100644 index 0000000..7d7a0d7 --- /dev/null +++ b/src/main/scala/codechicken/microblock/ISidedHollowConnect.java @@ -0,0 +1,14 @@ +package codechicken.microblock; + +/** + * Implement this on center parts that connect through hollow covers to have the covers render their hollows to fit the + * connection size. + */ +public interface ISidedHollowConnect { + + /** + * @param side The side of the block on which the cover resides + * @return The size (width and height) of the connection in pixels. Must be less than 12 and more than 0 + */ + int getHollowSize(int side); +} diff --git a/src/main/scala/codechicken/microblock/ISidedHollowConnect.scala b/src/main/scala/codechicken/microblock/ISidedHollowConnect.scala deleted file mode 100644 index 5515cd0..0000000 --- a/src/main/scala/codechicken/microblock/ISidedHollowConnect.scala +++ /dev/null @@ -1,15 +0,0 @@ -package codechicken.microblock - -/** Implement this on center parts that connect through hollow covers to have - * the covers render their hollows to fit the connection size - */ -trait ISidedHollowConnect { - - /** @param side - * The side of the block on which the cover resides - * @return - * The size (width and height) of the connection in pixels. Must be be less - * than 12 and more than 0 - */ - def getHollowSize(side: Int): Int -} diff --git a/src/main/scala/codechicken/multipart/INeighborTileChange.java b/src/main/scala/codechicken/multipart/INeighborTileChange.java new file mode 100644 index 0000000..f5bbdb1 --- /dev/null +++ b/src/main/scala/codechicken/multipart/INeighborTileChange.java @@ -0,0 +1,14 @@ +package codechicken.multipart; + +/** + * Mixin interface for parts that want to be notified of neighbor tile change events (comparators or inventory + * maintainers). + */ +public interface INeighborTileChange { + + /** Returns whether this part needs calls for tile changes through one solid block. */ + boolean weakTileChanges(); + + /** Callback for neighbor tile changes, from same function in Block. */ + void onNeighborTileChanged(int side, boolean weak); +} diff --git a/src/main/scala/codechicken/multipart/INeighborTileChange.scala b/src/main/scala/codechicken/multipart/INeighborTileChange.scala deleted file mode 100644 index ca4ec2f..0000000 --- a/src/main/scala/codechicken/multipart/INeighborTileChange.scala +++ /dev/null @@ -1,16 +0,0 @@ -package codechicken.multipart - -/** Mixin interface for parts that want to be notified of neighbor tile change - * events (comparators or inventory maintainers) - */ -trait INeighborTileChange { - - /** Returns whether this part needs calls for tile changes through one solid - * block - */ - def weakTileChanges(): Boolean - - /** Callback for neighbor tile changes, from same function in Block - */ - def onNeighborTileChanged(side: Int, weak: Boolean) -} diff --git a/src/main/scala/codechicken/multipart/IRandomDisplayTick.java b/src/main/scala/codechicken/multipart/IRandomDisplayTick.java new file mode 100644 index 0000000..3cc761a --- /dev/null +++ b/src/main/scala/codechicken/multipart/IRandomDisplayTick.java @@ -0,0 +1,14 @@ +package codechicken.multipart; + +import java.util.Random; + +/** + * Interface for parts that need random display ticks (torches). + *

+ * Marker interface for TRandomDisplayTickTile. + */ +public interface IRandomDisplayTick { + + /** Called on a random display tick. */ + void randomDisplayTick(Random random); +} diff --git a/src/main/scala/codechicken/multipart/IRandomDisplayTick.scala b/src/main/scala/codechicken/multipart/IRandomDisplayTick.scala deleted file mode 100644 index e2dcb4e..0000000 --- a/src/main/scala/codechicken/multipart/IRandomDisplayTick.scala +++ /dev/null @@ -1,14 +0,0 @@ -package codechicken.multipart - -import java.util.Random - -/** Interface for parts that need random display ticks (torches) - * - * Marker interface for TRandomDisplayTickTile - */ -trait IRandomDisplayTick { - - /** Called on a random display tick. - */ - def randomDisplayTick(random: Random) -} diff --git a/src/main/scala/codechicken/multipart/TRandomUpdateTick.java b/src/main/scala/codechicken/multipart/TRandomUpdateTick.java new file mode 100644 index 0000000..6940df1 --- /dev/null +++ b/src/main/scala/codechicken/multipart/TRandomUpdateTick.java @@ -0,0 +1,30 @@ +package codechicken.multipart; + +/** + * Interface for parts with random update ticks. Used in conjunction with TickScheduler. + *

+ * The Scala trait extended TMultiPart, but a trait extending a class is a bare interface in bytecode, so implementors + * must extend TMultiPart themselves. + */ +public interface TRandomUpdateTick { + + /** + * Called on random update. Random ticks are between 800 and 1600 ticks from their last scheduled/random tick. + */ + void randomUpdate(); + + /** + * Registers the part for random ticking. This cannot be a default method: TMultiPart declares onWorldJoin, and a + * superclass method always beats an interface default, so the default would never run. Every implementor must + * declare it and call {@link TickScheduler#loadRandomTick(TRandomUpdateTick)} itself. + * + *

+     * 
+     * @Override
+     * public void onWorldJoin() {
+     *     TickScheduler.loadRandomTick(this);
+     * }
+     * 
+ */ + void onWorldJoin(); +} diff --git a/src/main/scala/codechicken/multipart/TRandomUpdateTick.scala b/src/main/scala/codechicken/multipart/TRandomUpdateTick.scala deleted file mode 100644 index 9a5f9e1..0000000 --- a/src/main/scala/codechicken/multipart/TRandomUpdateTick.scala +++ /dev/null @@ -1,19 +0,0 @@ -package codechicken.multipart - -/** Interface for parts with random update ticks. Used in conjuction with - * TickScheduler - */ -trait TRandomUpdateTick extends TMultiPart { - - /** Called on random update. Random ticks are between 800 and 1600 ticks from - * their last scheduled/random tick - */ - def randomUpdate() - - /** If implementing interface in java, be sure to implement this method - * yourself - */ - override def onWorldJoin() { - TickScheduler.loadRandomTick(this) - } -} diff --git a/src/main/scala/codechicken/multipart/TSlottedPart.java b/src/main/scala/codechicken/multipart/TSlottedPart.java new file mode 100644 index 0000000..df5b3ac --- /dev/null +++ b/src/main/scala/codechicken/multipart/TSlottedPart.java @@ -0,0 +1,16 @@ +package codechicken.multipart; + +/** + * Interface for parts that fill a slot based configuration as defined in PartMap. If this is implemented, calling + * partMap(slot) on the host tile will return this part if the corresponding bit in the slotMask is set. + *

+ * Marker interface for TSlottedTile. + *

+ * The Scala trait extended TMultiPart, but a trait extending a class is a bare interface in bytecode, so implementors + * must extend TMultiPart themselves. + */ +public interface TSlottedPart { + + /** A bitmask of slots that this part fills. Slot x is 1<<x. */ + int getSlotMask(); +} diff --git a/src/main/scala/codechicken/multipart/TSlottedPart.scala b/src/main/scala/codechicken/multipart/TSlottedPart.scala deleted file mode 100644 index 192e30d..0000000 --- a/src/main/scala/codechicken/multipart/TSlottedPart.scala +++ /dev/null @@ -1,14 +0,0 @@ -package codechicken.multipart - -/** Interface for parts that fill a slot based configuration as defined in - * PartMap. If this is implemented, calling partMap(slot) on the host tile will - * return this part if the corresponding bit in the slotMask is set - * - * Marker interface for TSlottedTile - */ -trait TSlottedPart extends TMultiPart { - - /** a bitmask of slots that this part fills. slot x is 1<