diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index 77c4e8b..86390f4 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -489,3 +489,73 @@ and deliberately ignore overrides. Both behaviors are preserved as found and are - Complete plain-JVM suite: 65 tests, 0 failures, 0 errors. - Clean `spotlessApply checkstyleTest build`: passing. - Java 8 Forge dedicated-server suite: 3 tests, 0 failures, 0 errors. + +## 2026-08-14 — MicroMaterialRegistry Java port + +The first stateful registry converted, and the widest surface so far: ten jars reference the type and three more reach +it reflectively. Covers `MicroMaterialRegistry`, `MicroMaterialRegistry$` and the two nested interfaces. + +### Observable behavior + +No known divergence. `setupIDMap` still sorts the type map by name into the id array so ids are indexes into it, +`materialID` and `materialName` still round trip, `remapName` still resolves on both `getMaterial(String)` and +`materialID`, `getMissingId` still fails loudly when the placeholder is absent rather than falling back to zero, ids +still round trip through the shared `IDWriter`, and a highlight renderer that claims the highlight still short-circuits +the default renderer. + +`calcMaxCuttingStrength` still throws when no saw is registered. The reference reached that through `max` on an empty +collection; the port throws `UnsupportedOperationException` explicitly to keep the same failure rather than silently +leaving the field at zero. + +### Changed API: readIDMap + +Accepted divergence: `readIDMap` returns `java.util.List` instead of `scala.collection.Seq`. No jar in +the pack references it, and the only caller is the in-repo material registration packet handler, which was updated +from `mkString` to `String.join`. This removes one of the five Scala-typed descriptors the ABI inventory identified. + +`getIdMap()[Lscala/Tuple2;` is deliberately **not** changed, because extrautilities links against it. The registry +therefore still stores `scala.Tuple2` internally rather than converting on every call. + +### Removed API + +- The three mangled accessors Scala emitted as public so its closure classes could reach private state: + `codechicken$microblock$MicroMaterialRegistry$$idMap()`, `$$typeMap()` and `$$nameMap()`. They are compiler + artifacts, not API, and no jar references them. +- `MicroMaterialRegistry$IMicroMaterial$class`, along with its four statics. No jar references it. All ten + implementors reach `IMicroMaterial` through `BlockMicroMaterial`, which is in-repo, so nothing external carries + forwarders to the helper. +- `MissingMicroMaterial.canRenderInPass(int)` and `MissingMicroMaterial.isSolid()` static forwarders, plus the + matching instance methods on `MissingMicroMaterial$`. That object overrides neither, so with the members now being + Java interface defaults Scala emits no forwarder and therefore no static forwarder either. Nothing in the pack + references `MissingMicroMaterial` at all. This is the same class of change as `ItemSaw` in the `Saw` port, and the + characterization caught it by failing to compile. + +### Added API + +`setupIDMap`, `calcMaxCuttingStrength` and `loadIcons` gain static forwarders on `MicroMaterialRegistry`. They were +`private[microblock]` in Scala, which is public on the JVM but produces no static forwarder. Additive. + +### IMicroMaterial defaults + +`loadIcons`, `canRenderInPass` and `isSolid` become real Java defaults. Neither nested interface has a superclass to +lose to, and both in-repo implementors, `BlockMicroMaterial` and `MissingMicroMaterial`, extend nothing else. Java +implementors can now inherit the three instead of restating them; both nested interfaces are descriptor-identical to +the reference. + +### Compiler artifacts + +Accepted divergence: all eleven `MicroMaterialRegistry$$anonfun$*` classes are removed. The Java loops produce no +replacement classes. + +### Validation + +- `MicroMaterialRegistryCharacterizationTest`: 7 tests, 0 failures, 0 errors, passing against both the Scala baseline + and the port. +- `MicroMaterialRegistryBinaryCompatibilityTest`: 1 test, 0 failures, 0 errors, driving a reference-compiled Scala + consumer through `MODULE$` and the raw `scala.Tuple2` array. +- Complete plain-JVM suite: 73 tests, 0 failures, 0 errors. +- Clean `spotlessApply checkstyleTest build`: passing. +- Java 8 Forge dedicated-server suite: 3 tests, 0 failures, 0 errors. This exercises the real path, since server + startup calls `setupIDMap`, `calcMaxCuttingStrength` and the default content registration. +- Error paths remain uncovered by tests: they call the microblock logger, which is null until `MicroblockProxy.preInit` + runs, so they cannot execute headless. They are unchanged from the reference. diff --git a/src/main/scala/codechicken/microblock/MicroMaterialRegistry$.java b/src/main/scala/codechicken/microblock/MicroMaterialRegistry$.java new file mode 100644 index 0000000..b540b2e --- /dev/null +++ b/src/main/scala/codechicken/microblock/MicroMaterialRegistry$.java @@ -0,0 +1,100 @@ +package codechicken.microblock; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MovingObjectPosition; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.packet.PacketCustom; +import codechicken.microblock.MicroMaterialRegistry.IMicroHighlightRenderer; +import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial; +import scala.Tuple2; + +/** + * Scala companion singleton. Retained because compiled Scala consumers read MODULE$ and call these instance methods. + */ +public final class MicroMaterialRegistry$ { + + public static final MicroMaterialRegistry$ MODULE$ = new MicroMaterialRegistry$(); + + private MicroMaterialRegistry$() {} + + public void registerMaterial(IMicroMaterial material, String name) { + MicroMaterialRegistry.registerMaterial(material, name); + } + + public void replaceMaterial(IMicroMaterial material, String name) { + MicroMaterialRegistry.replaceMaterial(material, name); + } + + public void registerHighlightRenderer(IMicroHighlightRenderer handler) { + MicroMaterialRegistry.registerHighlightRenderer(handler); + } + + public void remapName(String oldName, String newName) { + MicroMaterialRegistry.remapName(oldName, newName); + } + + public void setupIDMap() { + MicroMaterialRegistry.setupIDMap(); + } + + public int getMissingId() { + return MicroMaterialRegistry.getMissingId(); + } + + public void calcMaxCuttingStrength() { + MicroMaterialRegistry.calcMaxCuttingStrength(); + } + + public void loadIcons() { + MicroMaterialRegistry.loadIcons(); + } + + public int getMaxCuttingStrength() { + return MicroMaterialRegistry.getMaxCuttingStrength(); + } + + public void writeIDMap(PacketCustom packet) { + MicroMaterialRegistry.writeIDMap(packet); + } + + public List readIDMap(PacketCustom packet) { + return MicroMaterialRegistry.readIDMap(packet); + } + + public void writeMaterialID(MCDataOutput data, int id) { + MicroMaterialRegistry.writeMaterialID(data, id); + } + + public int readMaterialID(MCDataInput data) { + return MicroMaterialRegistry.readMaterialID(data); + } + + public String materialName(int id) { + return MicroMaterialRegistry.materialName(id); + } + + public int materialID(String name) { + return MicroMaterialRegistry.materialID(name); + } + + public IMicroMaterial getMaterial(String name) { + return MicroMaterialRegistry.getMaterial(name); + } + + public IMicroMaterial getMaterial(int id) { + return MicroMaterialRegistry.getMaterial(id); + } + + public Tuple2[] getIdMap() { + return MicroMaterialRegistry.getIdMap(); + } + + public boolean renderHighlight(EntityPlayer player, MovingObjectPosition hit, CommonMicroClass mcrClass, int size, + int material) { + return MicroMaterialRegistry.renderHighlight(player, hit, mcrClass, size, material); + } +} diff --git a/src/main/scala/codechicken/microblock/MicroMaterialRegistry.java b/src/main/scala/codechicken/microblock/MicroMaterialRegistry.java new file mode 100644 index 0000000..782fe2b --- /dev/null +++ b/src/main/scala/codechicken/microblock/MicroMaterialRegistry.java @@ -0,0 +1,285 @@ +package codechicken.microblock; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.minecraft.block.Block.SoundType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.MovingObjectPosition; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.packet.PacketCustom; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Vector3; +import codechicken.microblock.handler.MicroblockProxy; +import codechicken.multipart.IDWriter; +import codechicken.multipart.MultiPartRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import scala.Tuple2; + +public final class MicroMaterialRegistry { + + /** Interface for defining a micro material. */ + public interface IMicroMaterial { + + /** The icon to be used for breaking particles on side. */ + @SideOnly(Side.CLIENT) + IIcon getBreakingIcon(int side); + + /** Callback to load icons from the underlying block/etc. */ + @SideOnly(Side.CLIENT) + default void loadIcons() {} + + /** + * This function must configure and use the CCRenderState pipeline to draw from the current vertex source. + * CCRenderState.lightMatrix will be set. + * + * @param pass The current render pass, -1 for inventory rendering + * @param bounds The cuboid bounds of the face being rendered + */ + @SideOnly(Side.CLIENT) + void renderMicroFace(Vector3 pos, int pass, Cuboid6 bounds); + + /** Get the render pass for which this material renders in. */ + @SideOnly(Side.CLIENT) + default boolean canRenderInPass(int pass) { + return pass == 0; + } + + /** Return true if this material is not opaque (glass, ice). */ + boolean isTransparent(); + + /** Return the light level emitted by this material (glowstone). */ + int getLightValue(); + + /** Return the strength of this material. */ + float getStrength(EntityPlayer player); + + /** Return the localised name of this material (normally the block name). */ + String getLocalizedName(); + + /** Get the item that this material is cut from (full block to slabs). */ + ItemStack getItem(); + + /** Get the strength of saw requried to cut this material. */ + int getCutterStrength(); + + /** Get the breaking/walking sound. */ + SoundType getSound(); + + /** Return true if this material is solid and opaque (can run wires on etc). */ + default boolean isSolid() { + return !isTransparent(); + } + + /** Get the explosion resistance of this part to an explosion caused by entity. */ + float explosionResistance(Entity entity); + } + + /** + * Interface for overriding the default micro placement highlight handler to show the effect of placement on a + * certain block/part. + */ + public interface IMicroHighlightRenderer { + + /** Return true if a custom highlight was rendered and the default should be skipped. */ + boolean renderHighlight(EntityPlayer player, MovingObjectPosition hit, CommonMicroClass mcrClass, int size, + int material); + } + + private static final Map typeMap = new HashMap<>(); + private static final Map nameMap = new HashMap<>(); + private static Tuple2[] idMap; + private static final IDWriter idWriter = new IDWriter(); + + private static final List highlightRenderers = new ArrayList<>(); + private static int maxCuttingStrength; + + private static final Map remap = new HashMap<>(); + private static int missingId; + + private MicroMaterialRegistry() {} + + /** Register a micro material with unique identifier name. */ + public static void registerMaterial(IMicroMaterial material, String name) { + if (MultiPartRegistry.loaded()) { + throw new IllegalStateException("You must register your materials in the init methods."); + } + + if (typeMap.containsKey(name)) { + MicroblockProxy.logger().error("Material with id " + name + " is already registered."); + return; + } + + typeMap.put(name, material); + } + + /** Replace a micro material with unique identifier name. */ + public static void replaceMaterial(IMicroMaterial material, String name) { + if (MultiPartRegistry.loaded()) { + throw new IllegalStateException("You must register your materials in the init methods."); + } + + if (typeMap.remove(name) == null) { + MicroblockProxy.logger().error("Material with id " + name + " was not registered."); + } + + MicroblockProxy.logger().debug("Replaced micro material: " + name); + + typeMap.put(name, material); + } + + /** Registers a highlight renderer. */ + public static void registerHighlightRenderer(IMicroHighlightRenderer handler) { + highlightRenderers.add(handler); + } + + public static void remapName(String oldName, String newName) { + remap.put(oldName, newName); + } + + @SuppressWarnings("unchecked") + public static void setupIDMap() { + List> entries = new ArrayList<>(typeMap.entrySet()); + entries.sort(Comparator.comparing(Map.Entry::getKey)); + + idMap = new Tuple2[entries.size()]; + for (int i = 0; i < entries.size(); i++) { + idMap[i] = new Tuple2<>(entries.get(i).getKey(), entries.get(i).getValue()); + } + + idWriter.setMax(idMap.length); + nameMap.clear(); + for (int i = 0; i < idMap.length; i++) { + nameMap.put(idMap[i]._1(), i); + } + + Integer missing = nameMap.get(MissingMicroMaterial.key()); + if (missing == null) { + throw new IllegalStateException( + "MissingMicroMaterial is not registered; the placeholder must never fall back to id 0."); + } + missingId = missing; + } + + public static int getMissingId() { + return missingId; + } + + public static void calcMaxCuttingStrength() { + int max = Integer.MIN_VALUE; + boolean found = false; + for (Iterator it = Item.itemRegistry.iterator(); it.hasNext();) { + Object next = it.next(); + if (next instanceof Saw) { + max = Math.max(max, ((Saw) next).getMaxCuttingStrength()); + found = true; + } + } + if (!found) { + // Matches the reference, where max on an empty collection throws. + throw new UnsupportedOperationException("empty.max"); + } + maxCuttingStrength = max; + } + + public static void loadIcons() { + if (idMap != null) { + for (Tuple2 entry : idMap) { + entry._2().loadIcons(); + } + } + } + + public static int getMaxCuttingStrength() { + return maxCuttingStrength; + } + + public static void writeIDMap(PacketCustom packet) { + packet.writeInt(idMap.length); + for (Tuple2 entry : idMap) { + packet.writeString(entry._1()); + } + } + + /** Reads the server's id map, returning the names this client has no material for. */ + @SuppressWarnings("unchecked") + public static List readIDMap(PacketCustom packet) { + int k = packet.readInt(); + idWriter.setMax(k); + idMap = new Tuple2[k]; + nameMap.clear(); + List missing = new ArrayList<>(); + for (int i = 0; i < k; i++) { + String s = packet.readString(); + IMicroMaterial v = typeMap.get(s); + if (v == null) { + missing.add(s); + } else { + idMap[i] = new Tuple2<>(s, v); + nameMap.put(s, i); + } + } + return missing; + } + + public static void writeMaterialID(MCDataOutput data, int id) { + idWriter.write(data, id); + } + + public static int readMaterialID(MCDataInput data) { + return idWriter.read(data); + } + + public static String materialName(int id) { + return idMap[id]._1(); + } + + public static int materialID(String name) { + Integer id = nameMap.get(remapped(name)); + if (id == null) { + MicroblockProxy.logger().error("Missing mapping for part with ID: " + name); + return missingId; + } + return id; + } + + public static IMicroMaterial getMaterial(String name) { + return typeMap.get(remapped(name)); + } + + public static IMicroMaterial getMaterial(int id) { + return idMap[id]._2(); + } + + public static Tuple2[] getIdMap() { + return idMap; + } + + public static boolean renderHighlight(EntityPlayer player, MovingObjectPosition hit, CommonMicroClass mcrClass, + int size, int material) { + for (IMicroHighlightRenderer renderer : highlightRenderers) { + if (renderer.renderHighlight(player, hit, mcrClass, size, material)) { + return true; + } + } + + MicroblockRender.renderHighlight(player, hit, mcrClass, size, material); + return true; + } + + private static String remapped(String name) { + String replacement = remap.get(name); + return replacement == null ? name : replacement; + } +} diff --git a/src/main/scala/codechicken/microblock/MicroMaterialRegistry.scala b/src/main/scala/codechicken/microblock/MicroMaterialRegistry.scala deleted file mode 100644 index ca9f314..0000000 --- a/src/main/scala/codechicken/microblock/MicroMaterialRegistry.scala +++ /dev/null @@ -1,255 +0,0 @@ -package codechicken.microblock - -import codechicken.multipart.IDWriter -import codechicken.lib.packet.PacketCustom -import scala.collection.mutable.HashMap -import codechicken.lib.data.MCDataOutput -import codechicken.lib.data.MCDataInput -import codechicken.multipart.MultiPartRegistry -import cpw.mods.fml.relauncher.SideOnly -import cpw.mods.fml.relauncher.Side -import codechicken.lib.vec.{Cuboid6, Vector3} -import net.minecraft.util.IIcon -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.item.{Item, ItemStack} -import scala.collection.mutable.ListBuffer -import net.minecraft.util.MovingObjectPosition -import net.minecraft.entity.Entity -import scala.collection.JavaConversions._ -import java.util.{Iterator => JIterator} -import net.minecraft.block.Block.SoundType - -object MicroMaterialRegistry { - - /** Interface for defining a micro material - */ - trait IMicroMaterial { - - /** The icon to be used for breaking particles on side - */ - @SideOnly(Side.CLIENT) - def getBreakingIcon(side: Int): IIcon - - /** Callback to load icons from the underlying block/etc - */ - @SideOnly(Side.CLIENT) - def loadIcons() {} - - /** This function must configure and use the CCRenderState pipeline to draw - * from the current vertex source. CCRenderState.lightMatrix will be set. - * @param pass - * The current render pass, -1 for inventory rendering - * @param bounds - * The cuboid bounds of the face being rendered - */ - @SideOnly(Side.CLIENT) - def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) - - /** Get the render pass for which this material renders in. - */ - @SideOnly(Side.CLIENT) - def canRenderInPass(pass: Int) = pass == 0 - - /** Return true if this material is not opaque (glass, ice). - */ - def isTransparent: Boolean - - /** Return the light level emitted by this material (glowstone) - */ - def getLightValue: Int - - /** Return the strength of this material - */ - def getStrength(player: EntityPlayer): Float - - /** Return the localised name of this material (normally the block name) - */ - def getLocalizedName: String - - /** Get the item that this material is cut from (full block -> slabs) - */ - def getItem: ItemStack - - /** Get the strength of saw requried to cut this material - */ - def getCutterStrength: Int - - /** Get the breaking/walking sound - */ - def getSound: SoundType - - /** Return true if this material is solid and opaque (can run wires on etc) - */ - def isSolid = !isTransparent - - /** Get the explosion resistance of this part to an explosion caused by - * entity - */ - def explosionResistance(entity: Entity): Float - } - - /** Interface for overriding the default micro placement highlight handler to - * show the effect of placement on a certain block/part - */ - trait IMicroHighlightRenderer { - - /** Return true if a custom highlight was rendered and the default should be - * skipped - */ - def renderHighlight( - player: EntityPlayer, - hit: MovingObjectPosition, - mcrClass: CommonMicroClass, - size: Int, - material: Int - ): Boolean - } - - private val typeMap = HashMap[String, IMicroMaterial]() - private val nameMap = HashMap[String, Int]() - private var idMap: Array[(String, IMicroMaterial)] = _ - private val idWriter = new IDWriter - - private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]() - private var maxCuttingStrength: Int = _ - - private val remap = HashMap[String, String]() - private var missingId: Int = 0 - - /** Register a micro material with unique identifier name - */ - def registerMaterial(material: IMicroMaterial, name: String) { - if (MultiPartRegistry.loaded) - throw new IllegalStateException( - "You must register your materials in the init methods." - ) - - if (typeMap.contains(name)) { - logger.error("Material with id " + name + " is already registered.") - return - } - - typeMap.put(name, material) - } - - /** Replace a micro material with unique identifier name - */ - def replaceMaterial(material: IMicroMaterial, name: String) { - if (MultiPartRegistry.loaded) - throw new IllegalStateException( - "You must register your materials in the init methods." - ) - - if (typeMap.remove(name).isEmpty) - logger.error("Material with id " + name + " was not registered.") - - logger.debug("Replaced micro material: " + name) - - typeMap.put(name, material) - } - - /** Registers a highlight renderer - */ - def registerHighlightRenderer(handler: IMicroHighlightRenderer) { - highlightRenderers += handler - } - - def remapName(oldName: String, newName: String): Unit = - remap.put(oldName, newName) - - private[microblock] def setupIDMap() { - idMap = typeMap.toList.sortBy(_._1).toArray - idWriter.setMax(idMap.length) - nameMap.clear() - for (i <- 0 until idMap.length) - nameMap.put(idMap(i)._1, i) - missingId = nameMap.getOrElse( - MissingMicroMaterial.key, - throw new IllegalStateException( - "MissingMicroMaterial is not registered; the placeholder must never " + - "fall back to id 0." - ) - ) - } - - def getMissingId = missingId - - private[microblock] def calcMaxCuttingStrength() { - val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]] - maxCuttingStrength = it.flatMap { - case saw: Saw => Some(saw.getMaxCuttingStrength) - case _ => None - }.max - } - - private[microblock] def loadIcons() { - if (idMap != null) - idMap.foreach(e => e._2.loadIcons()) - } - - def getMaxCuttingStrength = maxCuttingStrength - - def writeIDMap(packet: PacketCustom) { - packet.writeInt(idMap.size) - idMap.foreach(e => packet.writeString(e._1)) - } - - def readIDMap(packet: PacketCustom): Seq[String] = { - val k = packet.readInt() - idWriter.setMax(k) - idMap = new Array(k) - nameMap.clear() - val missing = ListBuffer[String]() - for (i <- 0 until k) { - val s = packet.readString() - val v = typeMap.get(s) - if (v.isEmpty) - missing += s - else { - idMap(i) = (s, v.get) - nameMap.put(s, i) - } - } - return missing - } - - def writeMaterialID(data: MCDataOutput, id: Int) { - idWriter.write(data, id) - } - - def readMaterialID(data: MCDataInput) = idWriter.read(data) - - def materialName(id: Int) = idMap(id)._1 - - def materialID(name: String) = - nameMap.get(remap.getOrElse(name, name)) match { - case Some(v) => v - case None => - logger.error("Missing mapping for part with ID: " + name) - missingId - } - - def getMaterial(name: String) = - typeMap.getOrElse(remap.getOrElse(name, name), null) - - def getMaterial(id: Int) = idMap(id)._2 - - def getIdMap = idMap - - def renderHighlight( - player: EntityPlayer, - hit: MovingObjectPosition, - mcrClass: CommonMicroClass, - size: Int, - material: Int - ): Boolean = { - val overridden = highlightRenderers.find( - _.renderHighlight(player, hit, mcrClass, size, material) - ) - if (overridden.isDefined) - return true - - MicroblockRender.renderHighlight(player, hit, mcrClass, size, material) - return true - } -} diff --git a/src/main/scala/codechicken/microblock/handler/packethandlers.scala b/src/main/scala/codechicken/microblock/handler/packethandlers.scala index 796105f..cda3a40 100644 --- a/src/main/scala/codechicken/microblock/handler/packethandlers.scala +++ b/src/main/scala/codechicken/microblock/handler/packethandlers.scala @@ -47,7 +47,7 @@ object MicroblockCPH extends MicroblockPH with IClientPacketHandler { new S40PacketDisconnect( new ChatComponentTranslation( "microblock.missing", - missing.mkString(", ") + String.join(", ", missing) ) ) ) diff --git a/src/test/java/codechicken/microblock/MicroMaterialRegistryCharacterizationTest.java b/src/test/java/codechicken/microblock/MicroMaterialRegistryCharacterizationTest.java index 6bae44f..19b1fde 100644 --- a/src/test/java/codechicken/microblock/MicroMaterialRegistryCharacterizationTest.java +++ b/src/test/java/codechicken/microblock/MicroMaterialRegistryCharacterizationTest.java @@ -123,12 +123,14 @@ class MicroMaterialRegistryCharacterizationTest { */ @Test void materialInterfaceSuppliesRenderPassAndSolidity() { - assertTrue(MissingMicroMaterial.canRenderInPass(0)); - assertFalse(MissingMicroMaterial.canRenderInPass(1)); - assertFalse(MissingMicroMaterial.canRenderInPass(-1)); + IMicroMaterial missing = MissingMicroMaterial$.MODULE$; - assertFalse(MissingMicroMaterial.isTransparent()); - assertTrue(MissingMicroMaterial.isSolid()); + assertTrue(missing.canRenderInPass(0)); + assertFalse(missing.canRenderInPass(1)); + assertFalse(missing.canRenderInPass(-1)); + + assertFalse(missing.isTransparent()); + assertTrue(missing.isSolid()); } private static MCDataInput input(byte[] bytes) {