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<