refactor: port microblock generator to Java

This commit is contained in:
Algent committed 2026-09-02 11:25:04 +02:00
1 parent 8d3010941c
commit 2f4972e6fd
6 files changed
+177 -36

No files matched your search

+9
View File
@@ -1202,3 +1202,12 @@ generated-trait compatibility work. Scala-runtime removal remains a later projec
- A clean build passes all 257 plain-JVM tests and the Java 8 Forge server passes all 110 tests. All seven supported
public surfaces and WR-CBE's static `recalcBounds` descriptor match the reference. `microblock/MicroblockGenerator.scala`
is next.
- Characterized `MicroblockGenerator` against untouched Scala. Three plain-JVM cases freeze its facade, companion,
nested material interface, inherited `ASMMixinFactory`/`ScratchBitSet` shape, replaceable thread-local scratch state
and load-bearing calls. One Forge case freezes the complete material-added external Scala-trait path.
- Ported the facade, companion and real public static nested `IGeneratedMaterial` interface directly to Java while
leaving the generator and ScalaSignature machinery unchanged. Scratch-bit reuse, base/client selection, material
callback ordering, boxed constructor argument and the ProjectRed Scala-trait registration path are unchanged.
- A clean build passes all 260 plain-JVM tests and the Java 8 Forge server passes all 111 tests. The same three runtime
classes and every callable public descriptor match the reference, including ProjectRed's companion registration
and GuideNH's exact companion `create` method. `multipart/MultipartGenerator.scala` is next.
+47
View File
@@ -2997,3 +2997,50 @@ inventory contains no reference to it.
- Raw `javap` comparison confirms identical callable public names/descriptors for all seven supported types and
bytecode-identical retained trait/helper disassembly.
- Actual neighbour-driven shrink and render-bound refresh remain on the manual checklist.
## 2026-09-02 — Microblock generator Java port
The `MicroblockGenerator` facade, companion and nested `IGeneratedMaterial` interface are now Java. The companion
still extends the unchanged Scala `ASMMixinFactory<Microblock>` and implements the unchanged Scala `ScratchBitSet`;
all ScalaSignature parsing, Scala-trait registration and generated-class compilation remain in the original ASM
subsystem.
### Observable behavior
No known runtime divergence. Each `create` still clears and reuses the current thread's scratch `BitSet`, selects the
microblock class's base trait, adds the client trait only for client construction, then lets an
`IGeneratedMaterial` amend that same set before construction. The material ID remains a boxed `Integer` in the sole
generated constructor argument.
The public scratch setter remains behaviorally replaceable despite being a compiler-oriented trait member. The
Forge fixture proves an external material receives only the expected initial base bit, adds a registered third-party
Scala trait, and produces a generated microblock with that trait's initialized state and dispatch.
### ABI and reflection compatibility
The facade, companion and nested material interface retain the same three binary names, hierarchy and callable
public names/descriptors. `IGeneratedMaterial` remains a real public static member of `MicroblockGenerator`; the
original pure abstract Scala trait emitted no `$class` helper, so none is removed.
ProjectRed's load-bearing `MicroblockGenerator$.registerTrait(Class): int` call remains exact, and the unchanged
`ASMMixinFactory` still recognizes and compiles its external `LightMicroblock` Scala trait. GuideNH's exact reflective
`MicroblockGenerator$.create(MicroblockClass, int, boolean)` lookup also remains exact. All inherited static
forwarders, including the Scala `Seq`-shaped `construct`, keep their raw descriptors and generic surface.
Accepted classfile-only differences are the Java facade's private constructor, the ordinary non-public Java class
initializer flag and removed Scala marker attributes. The private scratch `ThreadLocal` loses `ACC_FINAL`: Java
cannot assign a final instance field through the required public Scala-trait setter, and preserving the setter's
observable replacement behavior is more important than an unreferenced private modifier.
### Compiler artifacts
None. The clean reference and port jars contain the same three `MicroblockGenerator` classes.
### Validation
- `MicroblockGeneratorCharacterizationTest`: 3 plain-JVM tests, unchanged from the Scala baseline.
- Clean complete plain-JVM suite: 260 tests, 0 failures, 0 errors.
- Java 8 Forge dedicated-server suite: 111 tests, 0 failures, 0 errors, including material-driven addition,
initialization and dispatch of the external Scala microblock trait.
- Raw `javap` comparison confirms the exact three-class list and identical callable public names/descriptors.
- The existing ProjectRed illuminated-microblock row remains the full-pack/client manual checkpoint.
+16 -10
View File
@@ -12,8 +12,8 @@ what breaks, and what is left. The other documents hold the reasoning.
| `JAVA_MIGRATION_MANUAL_CHECKS.md` | What no automated test can cover, and must be checked by hand in a client |
| `JAVA_MIGRATION_PROFILE.md` | The focused baseline, first measured result, findings, and exact rerun command |
Branch: `algent/java`. Base: `master`. 146 commits including the API-cleanup plan and separate characterization and
port commits through micro occlusion.
Branch: `algent/java`. Base: `master`. 148 commits including the API-cleanup plan and separate characterization and
port commits through the microblock generator.
## The one rule that matters
@@ -67,7 +67,7 @@ awk -F'"' '/<testsuite /{t+=$4;f+=$8;e+=$10} END{print "tests="t" failures="f" e
grep -o 'tests="[0-9]*" skipped="[0-9]*" failures="[0-9]*" errors="[0-9]*"' run/server/junit-out/TEST-*.xml
```
Current baseline: **257 plain-JVM tests and 110 Java 8 Forge dedicated-server tests passing.** The ignored local server
Current baseline: **260 plain-JVM tests and 111 Java 8 Forge dedicated-server tests passing.** The ignored local server
EULA is accepted in this checkout. GitHub Actions runs the same self-validating Forge suite in a dependent job after
the shared GTNH build; keep both jobs required.
@@ -272,7 +272,7 @@ across the port. It is also where the two shim constraints in the gotchas list w
`rayTraceAll`'s index production is now characterized, closing the gap the read-path cleanup left: the index written
into `ExtendedMOP.data` is what `reduceMOP` hands back to every click, activate, harvest and pick block.
185 Java files, 16 Scala files, ~2,967 Scala lines left (non-blank; that is the metric this figure has always used).
189 Java files, 15 Scala files, 2,807 Scala lines left (non-blank; that is the metric this figure has always used).
## What is left, and in what order
@@ -526,17 +526,23 @@ transparency decisions, render masks, traversal ranges and the complete trait de
trait/helper disassembly is bytecode-identical; only the private shrink traversal closure disappears. WR-CBE's
load-bearing static `MicroOcclusion.recalcBounds(JMicroShrinkRender, Cuboid6)` call remains exact.
**High.** `MicroblockGenerator.scala` is next. Freeze its inherited `ASMMixinFactory`/`ScratchBitSet` singleton,
nested `IGeneratedMaterial` Scala trait, exact companion/reflection surface and fresh trait-bit selection before
choosing the smallest safe split. ProjectRed exercises external Scala-trait generation and GuideNH pins the exact
`create(MicroblockClass, int, boolean)` companion method. Do not combine this with `MultipartGenerator` or ASM work.
The microblock generator is now a Java facade, companion and real nested material interface over the unchanged Scala
`ASMMixinFactory` and `ScratchBitSet`. Three plain-JVM cases freeze all three runtime surfaces, scratch replacement
and clearing, and the load-bearing call opcodes. One Forge case covers the previously missing full path from an
`IGeneratedMaterial` callback through registered external Scala-trait initialization and dispatch. The runtime class
list and all callable public descriptors remain exact for ProjectRed and GuideNH.
**High.** `MultipartGenerator.scala` is next. Freeze its companion-only GuideNH entry point, all `private[multipart]`
members that must become public Java, trait registration maps, server/client exclusion flags, pass-through handling,
scratch-bit reuse, generated class selection and construction before choosing a split. Reuse the extensive existing
generated-tile fixtures and do not combine this with `MultipartMixinFactory` or the rest of ASM.
**Phase 5 is complete.** There are no Scala files left in `multipart/scalatraits/`. The client pair required the first
narrow generator relaxation: Java-trait parent linearization, explicit field-accessor recognition, and exclusion of
transient runtime caches from generated copying.
**Phase 6/7, last.** `Microblock` and the microblock shape hierarchy, `MicroblockGenerator`, `MultipartGenerator`, and
all of `multipart/asm/`. The ASM subsystem should be last; freeze generated-class fixtures before touching it.
**Phase 6/7, last.** The retained generated microblock traits, `MultipartGenerator`, and all of `multipart/asm/`.
The ASM subsystem should be last; freeze generated-class fixtures before touching it.
**Pre-merge cleanup is explicit in `JAVA_MIGRATION.md`.** It covers relocating eligible Java files out of
`src/main/scala`, reconsidering forced Scala compilation, refreshing the README, organizing the durable migration
@@ -0,0 +1,57 @@
package codechicken.microblock;
import java.util.BitSet;
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial;
import codechicken.multipart.asm.ASMMixinFactory;
import codechicken.multipart.asm.ScratchBitSet;
import codechicken.multipart.asm.ScratchBitSet$class;
import scala.Predef$;
public final class MicroblockGenerator$ extends ASMMixinFactory<Microblock> implements ScratchBitSet {
public static final MicroblockGenerator$ MODULE$ = new MicroblockGenerator$();
private ThreadLocal<BitSet> codechicken$multipart$asm$ScratchBitSet$$bitSets;
private MicroblockGenerator$() {
super(Microblock.class, Predef$.MODULE$.wrapRefArray(new Class<?>[] { Integer.TYPE }));
ScratchBitSet$class.$init$(this);
}
@Override
public ThreadLocal<BitSet> codechicken$multipart$asm$ScratchBitSet$$bitSets() {
return codechicken$multipart$asm$ScratchBitSet$$bitSets;
}
@Override
public void codechicken$multipart$asm$ScratchBitSet$_setter_$codechicken$multipart$asm$ScratchBitSet$$bitSets_$eq(
ThreadLocal value) {
codechicken$multipart$asm$ScratchBitSet$$bitSets = value;
}
@Override
public BitSet getBitSet() {
return ScratchBitSet$class.getBitSet(this);
}
@Override
public BitSet freshBitSet() {
return ScratchBitSet$class.freshBitSet(this);
}
public Microblock create(MicroblockClass microClass, int materialId, boolean client) {
BitSet traits = freshBitSet();
traits.set(microClass.baseTraitId());
if (client) {
traits.set(microClass.clientTraitId());
}
IMicroMaterial material = MicroMaterialRegistry.getMaterial(materialId);
if (material instanceof MicroblockGenerator.IGeneratedMaterial) {
((MicroblockGenerator.IGeneratedMaterial) material).addTraits(traits, microClass, client);
}
return construct(traits, Predef$.MODULE$.wrapRefArray(new Object[] { Integer.valueOf(materialId) }));
}
}
@@ -0,0 +1,48 @@
package codechicken.microblock;
import java.util.BitSet;
import scala.collection.Seq;
public final class MicroblockGenerator {
public interface IGeneratedMaterial {
void addTraits(BitSet traits, MicroblockClass microClass, boolean client);
}
private MicroblockGenerator() {}
public static Class<Microblock> baseType() {
return MicroblockGenerator$.MODULE$.baseType();
}
@SuppressWarnings("rawtypes")
public static Object construct(BitSet traits, Seq arguments) {
return MicroblockGenerator$.MODULE$.construct(traits, arguments);
}
public static int getId(String trait) {
return MicroblockGenerator$.MODULE$.getId(trait);
}
public static int registerTrait(Class<?> trait) {
return MicroblockGenerator$.MODULE$.registerTrait(trait);
}
public static int registerTrait(String trait) {
return MicroblockGenerator$.MODULE$.registerTrait(trait);
}
public static BitSet getBitSet() {
return MicroblockGenerator$.MODULE$.getBitSet();
}
public static BitSet freshBitSet() {
return MicroblockGenerator$.MODULE$.freshBitSet();
}
public static Microblock create(MicroblockClass microClass, int material, boolean client) {
return MicroblockGenerator$.MODULE$.create(microClass, material, client);
}
}
@@ -1,26 +0,0 @@
package codechicken.microblock
import codechicken.multipart.asm.{ScratchBitSet, ASMMixinFactory}
import java.util.BitSet
object MicroblockGenerator
extends ASMMixinFactory(classOf[Microblock], classOf[Int])
with ScratchBitSet {
trait IGeneratedMaterial {
def addTraits(traits: BitSet, mcrClass: MicroblockClass, client: Boolean)
}
def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = {
val bitset = freshBitSet
bitset.set(mcrClass.baseTraitId)
if (client) bitset.set(mcrClass.clientTraitId)
MicroMaterialRegistry.getMaterial(material) match {
case genMat: IGeneratedMaterial =>
genMat.addTraits(bitset, mcrClass, client)
case _ =>
}
construct(bitset, material: Integer)
}
}