diff --git a/src/functionalTest/java/codechicken/multipart/test/DefaultContentFunctionalTest.java b/src/functionalTest/java/codechicken/multipart/test/DefaultContentFunctionalTest.java index acc730f..fd5a8dd 100644 --- a/src/functionalTest/java/codechicken/multipart/test/DefaultContentFunctionalTest.java +++ b/src/functionalTest/java/codechicken/multipart/test/DefaultContentFunctionalTest.java @@ -3,6 +3,7 @@ package codechicken.multipart.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Field; import java.util.ArrayList; @@ -19,21 +20,28 @@ import net.minecraft.init.Blocks; import org.junit.jupiter.api.Test; +import codechicken.lib.vec.Cuboid6; import codechicken.microblock.BlockMicroMaterial; import codechicken.microblock.CommonMicroClass; import codechicken.microblock.CornerMicroClass$; import codechicken.microblock.EdgeMicroClass$; +import codechicken.microblock.FaceMicroClass; import codechicken.microblock.FaceMicroClass$; +import codechicken.microblock.FaceMicroblock; +import codechicken.microblock.FacePlacement$; +import codechicken.microblock.FacePlacementGrid$; import codechicken.microblock.GrassMicroMaterial; import codechicken.microblock.HollowMicroClass$; import codechicken.microblock.MicroMaterialRegistry; import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial; +import codechicken.microblock.Microblock; import codechicken.microblock.MissingMicroMaterial; import codechicken.microblock.MissingMicroMaterial$; import codechicken.microblock.PostMicroClass$; import codechicken.microblock.TopMicroMaterial; import codechicken.multipart.MultiPartRegistry; import codechicken.multipart.MultiPartRegistry.IPartFactory2; +import codechicken.multipart.TFacePart; import scala.Tuple2; class DefaultContentFunctionalTest { @@ -71,6 +79,46 @@ class DefaultContentFunctionalTest { assertSame(PostMicroClass$.MODULE$, factories.get("mcr_post")); } + @Test + void keepsFaceFactoryPlacementAndAllFortyTwoBounds() { + FaceMicroClass$ factory = FaceMicroClass$.MODULE$; + assertSame(factory, FacePlacement$.MODULE$.microClass()); + assertSame(FacePlacementGrid$.MODULE$, FacePlacement$.MODULE$.placementGrid()); + assertSame(factory.aBounds(), FaceMicroClass.aBounds()); + assertEquals("mcr_face", factory.getName()); + assertEquals(3, factory.itemSlot()); + assertEquals(1f, factory.getResistanceFactor()); + + Cuboid6[] bounds = factory.aBounds(); + assertEquals(256, bounds.length); + int populated = 0; + for (int side = 0; side < 6; side++) { + for (int size = 1; size < 8; size++) { + Cuboid6 expected = expectedFaceBounds(side, size / 8d); + Cuboid6 actual = bounds[size << 4 | side]; + assertCuboid(expected, actual); + populated++; + } + } + for (Cuboid6 bound : bounds) { + if (bound != null) { + populated--; + } + } + assertEquals(0, populated, "Only the 6 sides by 7 thicknesses may be populated"); + + Microblock generated = factory.create(false, 0); + assertTrue(generated instanceof FaceMicroblock); + assertTrue(generated instanceof TFacePart); + for (int side = 0; side < 6; side++) { + generated.setShape(3, side); + FaceMicroblock face = (FaceMicroblock) generated; + assertSame(bounds[3 << 4 | side], face.getBounds()); + assertEquals(side, face.getSlot()); + assertEquals(generated.getIMaterial().isSolid(), face.solid(side)); + } + } + @Test void registersTheExactOrderedBuiltInMaterialsAndRemaps() throws Exception { List expectedNames = new ArrayList<>(); @@ -142,6 +190,34 @@ class DefaultContentFunctionalTest { add(names, remaps, block, registeredName, maxMeta, block.getUnlocalizedName()); } + private static Cuboid6 expectedFaceBounds(int side, double thickness) { + switch (side) { + case 0: + return new Cuboid6(0, 0, 0, 1, thickness, 1); + case 1: + return new Cuboid6(0, 1 - thickness, 0, 1, 1, 1); + case 2: + return new Cuboid6(0, 0, 0, 1, 1, thickness); + case 3: + return new Cuboid6(0, 0, 1 - thickness, 1, 1, 1); + case 4: + return new Cuboid6(0, 0, 0, thickness, 1, 1); + case 5: + return new Cuboid6(1 - thickness, 0, 0, 1, 1, 1); + default: + throw new AssertionError(side); + } + } + + private static void assertCuboid(Cuboid6 expected, Cuboid6 actual) { + assertEquals(expected.min.x, actual.min.x, 1e-12); + assertEquals(expected.min.y, actual.min.y, 1e-12); + assertEquals(expected.min.z, actual.min.z, 1e-12); + assertEquals(expected.max.x, actual.max.x, 1e-12); + assertEquals(expected.max.y, actual.max.y, 1e-12); + assertEquals(expected.max.z, actual.max.z, 1e-12); + } + private static void add(List names, Map remaps, Block block, String registeredName, int maxMeta, String oldName) { for (int meta = 0; meta <= maxMeta; meta++) { diff --git a/src/test/java/codechicken/microblock/FaceMicroblockCharacterizationTest.java b/src/test/java/codechicken/microblock/FaceMicroblockCharacterizationTest.java new file mode 100644 index 0000000..d6f5f96 --- /dev/null +++ b/src/test/java/codechicken/microblock/FaceMicroblockCharacterizationTest.java @@ -0,0 +1,179 @@ +package codechicken.microblock; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; + +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Type; + +import codechicken.lib.vec.Cuboid6; +import codechicken.multipart.TFacePart; + +class FaceMicroblockCharacterizationTest { + + private static final Set PLACEMENT_FACADE_METHODS = signatures( + "customPlacement(Lcodechicken/microblock/MicroblockPlacement;)Lcodechicken/microblock/ExecutablePlacement;", + "expand(II)Z", + "microClass()Lcodechicken/microblock/FaceMicroClass$;", + "opposite(II)I", + "placementGrid()Lcodechicken/microblock/FacePlacementGrid$;", + "sneakOpposite(II)Z"); + private static final Set PLACEMENT_COMPANION_METHODS = signatures( + "expand(II)Z", + "microClass()Lcodechicken/microblock/FaceMicroClass$;", + "microClass()Lcodechicken/microblock/MicroblockClass;", + "opposite(II)I", + "placementGrid()Lcodechicken/microblock/FacePlacementGrid$;", + "placementGrid()Lcodechicken/microblock/PlacementGrid;", + "sneakOpposite(II)Z"); + private static final Set CLASS_FACADE_METHODS = signatures( + "aBounds()[Lcodechicken/lib/vec/Cuboid6;", + "aBounds_$eq([Lcodechicken/lib/vec/Cuboid6;)V", + "baseTrait()Ljava/lang/Class;", + "baseTraitId()I", + "clientTrait()Ljava/lang/Class;", + "clientTraitId()I", + "create(ZI)Lcodechicken/microblock/Microblock;", + "createPart(Ljava/lang/String;Lcodechicken/lib/data/MCDataInput;)Lcodechicken/microblock/Microblock;", + "createPart(Ljava/lang/String;Lnet/minecraft/nbt/NBTTagCompound;)Lcodechicken/microblock/Microblock;", + "getClassId()I", + "getName()Ljava/lang/String;", + "getResistanceFactor()F", + "itemSlot()I", + "placementProperties()Lcodechicken/microblock/FacePlacement$;", + "register()V", + "register(I)V"); + private static final Set CLASS_COMPANION_METHODS = signatures( + "aBounds()[Lcodechicken/lib/vec/Cuboid6;", + "aBounds_$eq([Lcodechicken/lib/vec/Cuboid6;)V", + "baseTrait()Ljava/lang/Class;", + "clientTrait()Ljava/lang/Class;", + "getName()Ljava/lang/String;", + "getResistanceFactor()F", + "itemSlot()I", + "placementProperties()Lcodechicken/microblock/FacePlacement$;", + "placementProperties()Lcodechicken/microblock/PlacementProperties;"); + + @Test + void keepsFacadesCompanionsAndGeneratedTraitSurfaces() throws Exception { + assertFacade(FacePlacement.class, true, PLACEMENT_FACADE_METHODS); + assertCompanion(FacePlacement$.class, PlacementProperties.class, PLACEMENT_COMPANION_METHODS); + assertEquals(1, FacePlacement$.class.getDeclaredFields().length); + + assertFacade(FaceMicroClass.class, false, CLASS_FACADE_METHODS); + Constructor constructor = FaceMicroClass.class.getDeclaredConstructor(); + assertTrue(Modifier.isPublic(constructor.getModifiers())); + assertEquals(1, FaceMicroClass.class.getDeclaredConstructors().length); + assertCompanion(FaceMicroClass$.class, CommonMicroClass.class, CLASS_COMPANION_METHODS); + Field bounds = FaceMicroClass$.class.getDeclaredField("aBounds"); + assertSame(Cuboid6[].class, bounds.getType()); + assertTrue(Modifier.isPrivate(bounds.getModifiers())); + assertFalse(Modifier.isFinal(bounds.getModifiers())); + assertEquals(2, FaceMicroClass$.class.getDeclaredFields().length); + + assertTrait( + FaceMicroblock.class, + new Class[] { CommonMicroblock.class, TFacePart.class }, + signatures( + "getBounds()Lcodechicken/lib/vec/Cuboid6;", + "microClass()Lcodechicken/microblock/FaceMicroClass$;", + "solid(I)Z")); + assertHelper( + "codechicken.microblock.FaceMicroblock$class", + signatures( + "$init$(Lcodechicken/microblock/FaceMicroblock;)V", + "getBounds(Lcodechicken/microblock/FaceMicroblock;)Lcodechicken/lib/vec/Cuboid6;", + "microClass(Lcodechicken/microblock/FaceMicroblock;)Lcodechicken/microblock/FaceMicroClass$;", + "solid(Lcodechicken/microblock/FaceMicroblock;I)Z")); + + assertTrait( + FaceMicroblockClient.class, + new Class[] { CommonMicroblockClient.class }, + signatures("render(Lcodechicken/lib/vec/Vector3;I)V")); + assertHelper( + "codechicken.microblock.FaceMicroblockClient$class", + signatures( + "$init$(Lcodechicken/microblock/FaceMicroblockClient;)V", + "render(Lcodechicken/microblock/FaceMicroblockClient;Lcodechicken/lib/vec/Vector3;I)V")); + } + + @Test + void keepsFacePlacementRulesAndGridIdentity() { + FacePlacement$ placement = FacePlacement$.MODULE$; + assertSame(FacePlacementGrid$.MODULE$, placement.placementGrid()); + assertSame(FacePlacementGrid$.MODULE$, FacePlacement.placementGrid()); + assertNull(placement.customPlacement(null)); + assertNull(FacePlacement.customPlacement(null)); + + for (int slot = 0; slot < 6; slot++) { + for (int side = 0; side < 6; side++) { + boolean sneaking = slot == (side ^ 1); + assertEquals(slot ^ 1, placement.opposite(slot, side)); + assertEquals(slot ^ 1, FacePlacement.opposite(slot, side)); + assertEquals(sneaking, placement.sneakOpposite(slot, side)); + assertEquals(sneaking, FacePlacement.sneakOpposite(slot, side)); + assertEquals(sneaking, placement.expand(slot, side)); + assertEquals(sneaking, FacePlacement.expand(slot, side)); + } + } + } + + private static void assertFacade(Class type, boolean isFinal, Set methods) { + assertTrue(Modifier.isPublic(type.getModifiers())); + assertEquals(isFinal, Modifier.isFinal(type.getModifiers())); + assertSame(Object.class, type.getSuperclass()); + assertEquals(0, type.getDeclaredFields().length); + assertEquals(methods, publicDeclaredMethods(type)); + } + + private static void assertCompanion(Class type, Class superclass, Set methods) throws Exception { + assertTrue(Modifier.isPublic(type.getModifiers())); + assertTrue(Modifier.isFinal(type.getModifiers())); + assertSame(superclass, type.getSuperclass()); + assertEquals(methods, publicDeclaredMethods(type)); + Field module = type.getField("MODULE$"); + assertSame(type, module.getType()); + assertTrue(Modifier.isStatic(module.getModifiers())); + assertTrue(Modifier.isFinal(module.getModifiers())); + } + + private static void assertTrait(Class type, Class[] interfaces, Set methods) { + assertTrue(type.isInterface()); + assertArrayEquals(interfaces, type.getInterfaces()); + assertEquals(methods, publicDeclaredMethods(type)); + } + + private static void assertHelper(String name, Set methods) throws Exception { + Class type = Class.forName(name, false, FaceMicroblockCharacterizationTest.class.getClassLoader()); + assertTrue(Modifier.isPublic(type.getModifiers())); + assertTrue(Modifier.isAbstract(type.getModifiers())); + assertSame(Object.class, type.getSuperclass()); + assertEquals(methods, publicDeclaredMethods(type)); + } + + private static Set publicDeclaredMethods(Class type) { + Set methods = new TreeSet<>(); + for (Method method : type.getDeclaredMethods()) { + if (Modifier.isPublic(method.getModifiers())) { + methods.add(method.getName() + Type.getMethodDescriptor(method)); + } + } + return methods; + } + + private static Set signatures(String... signatures) { + return new TreeSet<>(Arrays.asList(signatures)); + } +}