diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index 7e5b0bb..7df6058 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -424,3 +424,6 @@ These ranges were formed before designing the characterization suite and should - Completed the first behavior-preserving production conversion by replacing `IDWriter.scala` with `IDWriter.java`. All eight existing encoding cases, all 25 plain-JVM tests, the clean build, and both Java 8 Forge server checks pass unchanged. - Preserved the four legacy Scala function accessor descriptors as deprecated binary bridges and added direct Java `write(MCDataOutput, int)` and `read(MCDataInput)` methods. Recompiled Scala registry callers use the new primitive methods because Scala property auto-application does not apply to accessors declared in Java. - Removed the six `IDWriter$$anonfun$setMax$*` compiler artifacts from the packaged jar. Their disappearance and replacement with Java anonymous helper classes is recorded in `JAVA_MIGRATION_DIVERGENCES.md`; the supported `IDWriter` descriptors remain link-compatible. +- Added a frozen Scala 2.11.5 consumer compiled against the reference dev jar. Its constructor and inherited default method call `JPartialOcclusion$class` directly, so it detects the linkage failure that freshly recompiled tests would miss. +- Replaced `TPartialOcclusion.scala` with Java implementations of `PartialOcclusionTest` and `JPartialOcclusion`. The interface keeps its name and method descriptors, while `allowCompleteOcclusion()` is now a Java default method and the deprecated `$class` helper remains for old Scala binaries. +- Passed all ten partial-occlusion behavior/API cases, the frozen Scala binary consumer, all 27 plain-JVM tests, a clean build, and both Java 8 Forge server checks. The existing marker-interface registration remains unchanged and continues to drive runtime tile generation. diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index 779a908..f6b3ba8 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -25,3 +25,28 @@ Accepted divergence: the six public-but-generated `IDWriter$$anonfun$setMax$1` t - Clean `checkstyleTest build`: passing. - Java 8 Forge dedicated-server suite: 2 tests, 0 failures, 0 errors. - `IDWriter` class-file version: 52 (Java 8). + +## 2026-08-14 — Partial occlusion Java port + +### Observable behavior + +No known voxel-test divergence. Grid resolution, coordinate rounding, owner encoding, conflict marking, required visibility, complete-occlusion exemption, and same-part overlap behavior remain unchanged. + +### Supported JVM API + +- Preserved every public `PartialOcclusionTest` constructor, accessor, `fill` overload, and `apply()` descriptor. Its four private fields also retain their reference names and descriptors. +- Preserved the `JPartialOcclusion` name plus both method descriptors. `allowCompleteOcclusion()` changes from a Scala-trait abstract method to a Java default method returning `false`. +- Retained deprecated `JPartialOcclusion$class` with the reference `allowCompleteOcclusion(JPartialOcclusion)` and `$init$(JPartialOcclusion)` static descriptors. This keeps Scala 2.11 implementations compiled against the reference jar linkable. + +### Compiler artifacts + +Accepted divergence: the five generated `PartialOcclusionTest$$anonfun$*` classes are removed. The Java loops produce no replacement helper classes, and these closure implementation details are not retained as supported API. + +### Validation + +- `PartialOcclusionCharacterizationTest`: 10 tests, 0 failures, 0 errors. +- Reference-compiled Scala consumer: 1 test, 0 failures, 0 errors. +- Complete plain-JVM suite: 27 tests, 0 failures, 0 errors. +- Clean `checkstyleTest build`: passing. +- Java 8 Forge dedicated-server suite: 2 tests, 0 failures, 0 errors. +- `PartialOcclusionTest` class-file version: 52 (Java 8). diff --git a/src/main/java/codechicken/multipart/JPartialOcclusion$class.java b/src/main/java/codechicken/multipart/JPartialOcclusion$class.java new file mode 100644 index 0000000..e969463 --- /dev/null +++ b/src/main/java/codechicken/multipart/JPartialOcclusion$class.java @@ -0,0 +1,14 @@ +package codechicken.multipart; + +/** Binary bridge for Scala implementations compiled against the original trait. */ +@Deprecated +public abstract class JPartialOcclusion$class { + + private JPartialOcclusion$class() {} + + public static boolean allowCompleteOcclusion(JPartialOcclusion instance) { + return false; + } + + public static void $init$(JPartialOcclusion instance) {} +} diff --git a/src/main/java/codechicken/multipart/JPartialOcclusion.java b/src/main/java/codechicken/multipart/JPartialOcclusion.java new file mode 100644 index 0000000..0c5452b --- /dev/null +++ b/src/main/java/codechicken/multipart/JPartialOcclusion.java @@ -0,0 +1,15 @@ +package codechicken.multipart; + +import codechicken.lib.vec.Cuboid6; + +/** Java callbacks for partial occlusion testing. */ +public interface JPartialOcclusion { + + /** Returns the partial occlusion boxes. */ + Iterable getPartialOcclusionBoxes(); + + /** Returns whether this part may be completely obscured. */ + default boolean allowCompleteOcclusion() { + return false; + } +} diff --git a/src/main/java/codechicken/multipart/PartialOcclusionTest.java b/src/main/java/codechicken/multipart/PartialOcclusionTest.java new file mode 100644 index 0000000..3757da8 --- /dev/null +++ b/src/main/java/codechicken/multipart/PartialOcclusionTest.java @@ -0,0 +1,78 @@ +package codechicken.multipart; + +import codechicken.lib.vec.Cuboid6; + +/** Tests whether every required part retains an exclusively owned voxel. */ +public class PartialOcclusionTest { + + private final int size; + private final int res = 8; + private final byte[] bits; + private final boolean[] partial; + + public PartialOcclusionTest(int size) { + this.size = size; + bits = new byte[res * res * res]; + partial = new boolean[size]; + } + + public int res() { + return res; + } + + public byte[] bits() { + return bits; + } + + public boolean[] partial() { + return partial; + } + + public void fill(int index, JPartialOcclusion part) { + fill(index, part.getPartialOcclusionBoxes(), part.allowCompleteOcclusion()); + } + + public void fill(int index, Iterable boxes, boolean complete) { + partial[index] = !complete; + for (Cuboid6 box : boxes) { + fill(index + 1, box); + } + } + + public void fill(int value, Cuboid6 box) { + int minX = (int) (box.min.x * res + 0.5); + int minY = (int) (box.min.y * res + 0.5); + int minZ = (int) (box.min.z * res + 0.5); + int maxX = (int) (box.max.x * res + 0.5); + int maxY = (int) (box.max.y * res + 0.5); + int maxZ = (int) (box.max.z * res + 0.5); + for (int x = minX; x < maxX; x++) { + for (int y = minY; y < maxY; y++) { + for (int z = minZ; z < maxZ; z++) { + int bitIndex = (x * res + y) * res + z; + if (bits[bitIndex] == 0) { + bits[bitIndex] = (byte) value; + } else { + bits[bitIndex] = -1; + } + } + } + } + } + + public boolean apply() { + boolean[] visible = new boolean[size]; + for (byte owner : bits) { + if (owner > 0) { + visible[owner - 1] = true; + } + } + + for (int index = 0; index < partial.length; index++) { + if (partial[index] && !visible[index]) { + return false; + } + } + return true; + } +} diff --git a/src/main/scala/codechicken/multipart/TPartialOcclusion.scala b/src/main/scala/codechicken/multipart/TPartialOcclusion.scala deleted file mode 100644 index 6933e81..0000000 --- a/src/main/scala/codechicken/multipart/TPartialOcclusion.scala +++ /dev/null @@ -1,73 +0,0 @@ -package codechicken.multipart - -import codechicken.lib.vec.Cuboid6 -import scala.collection.JavaConversions._ -import java.lang.Iterable - -/** This class provides a special type of occlusion model used by microblocks. - * The partial occlusion test defines bounding boxes that may intersect, so - * long as no part is completely obscured by a combination of the others. - * Partial bounding boxes may not intersect with normal bounding boxes from - * NormalOcclusionTest - * - * This test is actually managed by the mixin trait TPartialOcclusionTile which - * is generated when the marker interface JPartialOcclusion is found - */ -class PartialOcclusionTest(size: Int) { - - /** The resolution of the test, set to 1/8th of a block - */ - val res = 8 - val bits = new Array[Byte](res * res * res) - val partial = new Array[Boolean](size) - - def fill(i: Int, part: JPartialOcclusion) { - fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion) - } - - def fill(i: Int, boxes: Iterable[Cuboid6], complete: Boolean) { - partial(i) = !complete - boxes.foreach(box => fill(i + 1, box)) - } - - def fill(v: Int, box: Cuboid6) { - for (x <- (box.min.x * res + 0.5).toInt until (box.max.x * res + 0.5).toInt) - for ( - y <- (box.min.y * res + 0.5).toInt until (box.max.y * res + 0.5).toInt - ) - for ( - z <- (box.min.z * res + 0.5).toInt until (box.max.z * res + 0.5).toInt - ) { - val i = (x * res + y) * res + z - if (bits(i) == 0) - bits(i) = v.toByte - else - bits(i) = -1 - } - } - - def apply(): Boolean = { - val visible = new Array[Boolean](size) - bits.foreach(n => if (n > 0) visible(n - 1) = true) - - var i = 0 - while (i < partial.length) { - if (partial(i) && !visible(i)) - return false - i += 1 - } - - return true - } -} - -trait JPartialOcclusion { - - /** Return a list of partial occlusion boxes - */ - def getPartialOcclusionBoxes: Iterable[Cuboid6] - - /** Return true if this part may be completely obscured - */ - def allowCompleteOcclusion = false -} diff --git a/src/test/java/codechicken/multipart/PartialOcclusionCharacterizationTest.java b/src/test/java/codechicken/multipart/PartialOcclusionCharacterizationTest.java index 3010586..3877a40 100644 --- a/src/test/java/codechicken/multipart/PartialOcclusionCharacterizationTest.java +++ b/src/test/java/codechicken/multipart/PartialOcclusionCharacterizationTest.java @@ -64,6 +64,13 @@ class PartialOcclusionCharacterizationTest { assertTrue(test.apply()); } + @Test + void javaInterfaceDefaultsToRequiredVisibility() { + JPartialOcclusion part = Collections::emptyList; + + assertFalse(part.allowCompleteOcclusion()); + } + @Test void fullyOverlappingPartsLoseTheirVisibleVoxel() { PartialOcclusionTest test = new PartialOcclusionTest(2);