5.1 KiB
Testing part occlusion from Java
The algent/java branch adds TileMultipart.testOcclusion(Collection<? extends TMultiPart>, TMultiPart).
It tests a supplied collection of parts through the tile's existing occlusion hook, including generated
partial-occlusion checks. This addition is not yet tied to a minimum published dependency version.
Example
Use a NormallyOccludedPart to test a normal bounding shape against a supplied group of parts:
public static boolean fitsShape(TileMultipart tile, Collection<? extends TMultiPart> parts, Cuboid6 bounds) {
return tile.testOcclusion(parts, new NormallyOccludedPart(bounds));
}
The complete Java example compiles and runs
in the test suite. It also compiles with Scala excluded from its classpath and has no Scala bytecode references.
FMP still retains its Scala runtime internally. Collections of part subclasses, such as List<NormallyOccludedPart>,
are accepted without a cast.
Pass tile.jPartList() to test against the tile's currently stored parts, or pass a separate collection for a preview
or selected subset. To exercise a real candidate's custom occlusion behavior, pass that part instead of the shape
wrapper. The wrapper represents normal bounding geometry, not all the capabilities of a custom part.
What the query checks
The base tile tests each supplied part against the candidate, then tests the candidate against that same part. It proceeds in input iteration order and stops immediately when either direction rejects. Unbound/detached parts and duplicate entries participate; the candidate is not automatically excluded if it is also in the collection.
Generated partial-occlusion tiles retain their extra aggregate check. When the candidate implements JPartialOcclusion,
the existing trait checks the group plus the candidate before the ordinary pair callbacks. A plain TileMultipart
does not gain that capability just because the supplied parts need it. Use the appropriate composite tile; this method
does not generate or select tile traits.
This is an occlusion query, not full placement approval. It does not check placement permissions, duplicate membership,
slot occupancy or world replaceability, and does not bind/store parts or send notifications. For ordinary placement
and replacement, continue using canPlacePart, canAddPart and canReplacePart as appropriate. In particular,
canReplacePart(outgoing, candidate) performs its existing outgoing-part exclusion; this collection query does not.
Ownership, callbacks and failures
The Java entry copies the input into an immutable sequence before calling the legacy hook. Order, duplicates, null entries and part identities are preserved. Callback edits to the caller's collection do not change the current query's sequence. Part objects are shared, so mutations of their state can affect later callbacks. A nested query takes its own copy.
Copying failures, including a null collection, propagate before the legacy hook is called. There is no eager candidate/null-entry validation: with the base implementation, an empty collection accepts even a null candidate, and an early rejection can avoid a later null dereference. Callback exceptions propagate unchanged and stop the query; callback side effects already performed are not undone. Overrides may add checks or customize these boundaries.
Use the game thread when querying live parts. A geometric query may invoke arbitrary part callbacks, including ones that read or modify world/part state; it is not a thread-safety guarantee.
Overrides and migration
Dispatch is testOcclusion(Collection, candidate) → virtual occlusionTest(scala.collection.Seq, candidate).
The old method keeps its descriptor and body, and is deprecated for callers. Existing subclasses and generated traits
continue to override it. canAddPart and canReplacePart still call that hook directly; overriding only the new
Java method does not intercept their checks. The distinct Java name avoids introducing another Scala/Java overload
pair, following the loader compilation finding.
No direct consumer calls or overrides of the two-argument tile hook were found in the supplied source/ABI audit.
Consumers such as ProjectRed and ForgeRelocationFMP already use the Java-typed canReplacePart API and need no rename
for that contract. Part-level occlusionTest(TMultiPart) remains supported as-is. The Scala NormalOcclusionTest.apply
box-versus-box entry is a separate, still-used contract whose Java replacement remains pending.
Validation
JVM tests cover both directions, early rejection, original exceptions, null boundaries, snapshot ownership, duplicates, subtype/non-list collections, override routing and the shape example. A Forge test demonstrates that the generated partial trait rejects overlapping partial parts that plain pair tests accept. Existing descriptors, generated output, and frozen pre-change callers are checked separately. Actual client previews and full-pack adoption remain covered by the manual release checks.