From 23c6af379676ce5262c5c0cb6f1f331edc9b12c6 Mon Sep 17 00:00:00 2001 From: shipovskijkorp Date: Fri, 2 Oct 2026 11:11:19 +0300 Subject: [PATCH 1/6] 8.0.23 added anti-idiot defence --- .github/workflows/publish.yml | 10 +++++++ build-config/common.properties | 2 +- .../resources/META-INF/neoforge.mods.toml | 9 ++++++- .../src/main/java/buildcraft/lib/BCLib.java | 27 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 034cab72f..f03f51fe4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,6 +14,8 @@ concurrency: env: MODRINTH_PROJECT_ID: z3Aj5eOl CURSEFORGE_PROJECT_ID: '1592400' + MODRINTH_DEPENDENCIES: 'MhqIcygN(incompatible)' + CURSEFORGE_DEPENDENCIES: '421463(incompatible)' PUBLISH_DELAY_SECONDS: '60' jobs: @@ -166,10 +168,12 @@ jobs: modrinth-id: ${{ env.MODRINTH_PROJECT_ID }} modrinth-token: ${{ secrets.MODRINTH_TOKEN }} modrinth-changelog: ${{ github.event.release.body }} + modrinth-dependencies: ${{ env.MODRINTH_DEPENDENCIES }} curseforge-id: ${{ env.CURSEFORGE_PROJECT_ID }} curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} curseforge-changelog: ${{ github.event.release.body }} + curseforge-dependencies: ${{ env.CURSEFORGE_DEPENDENCIES }} github-token: ${{ secrets.GITHUB_TOKEN }} github-tag: ${{ github.event.release.tag_name }} @@ -193,10 +197,12 @@ jobs: modrinth-id: ${{ env.MODRINTH_PROJECT_ID }} modrinth-token: ${{ secrets.MODRINTH_TOKEN }} modrinth-changelog: ${{ github.event.release.body }} + modrinth-dependencies: ${{ env.MODRINTH_DEPENDENCIES }} curseforge-id: ${{ env.CURSEFORGE_PROJECT_ID }} curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} curseforge-changelog: ${{ github.event.release.body }} + curseforge-dependencies: ${{ env.CURSEFORGE_DEPENDENCIES }} github-token: ${{ secrets.GITHUB_TOKEN }} github-tag: ${{ github.event.release.tag_name }} @@ -220,10 +226,12 @@ jobs: modrinth-id: ${{ env.MODRINTH_PROJECT_ID }} modrinth-token: ${{ secrets.MODRINTH_TOKEN }} modrinth-changelog: ${{ github.event.release.body }} + modrinth-dependencies: ${{ env.MODRINTH_DEPENDENCIES }} curseforge-id: ${{ env.CURSEFORGE_PROJECT_ID }} curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} curseforge-changelog: ${{ github.event.release.body }} + curseforge-dependencies: ${{ env.CURSEFORGE_DEPENDENCIES }} github-token: ${{ secrets.GITHUB_TOKEN }} github-tag: ${{ github.event.release.tag_name }} @@ -247,10 +255,12 @@ jobs: modrinth-id: ${{ env.MODRINTH_PROJECT_ID }} modrinth-token: ${{ secrets.MODRINTH_TOKEN }} modrinth-changelog: ${{ github.event.release.body }} + modrinth-dependencies: ${{ env.MODRINTH_DEPENDENCIES }} curseforge-id: ${{ env.CURSEFORGE_PROJECT_ID }} curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} curseforge-changelog: ${{ github.event.release.body }} + curseforge-dependencies: ${{ env.CURSEFORGE_DEPENDENCIES }} github-token: ${{ secrets.GITHUB_TOKEN }} github-tag: ${{ github.event.release.tag_name }} diff --git a/build-config/common.properties b/build-config/common.properties index 5b9e7fde0..ff5eb8c6e 100644 --- a/build-config/common.properties +++ b/build-config/common.properties @@ -2,7 +2,7 @@ common.mod.group=ct.buildcraft.ce common.mod.id=buildcraft common.mod.name=BuildCraft Community Edition -common.mod.version=8.0.22 +common.mod.version=8.0.23 common.mod.archive_name=BuildCraft-Community-Edition common.mod.authors=CurativeTree, ShipovskijKorp common.mod.license=MPL-2.0 diff --git a/source-platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/source-platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml index a2aaced22..8ea4f7ba0 100644 --- a/source-platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/source-platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -33,6 +33,13 @@ Shared library code and runtime helpers used by the BuildCraft Community Edition ordering="NONE" side="BOTH" +[[dependencies.buildcraftlib]] + modId="buildcraftrf" + type="incompatible" + reason="BuildCraft RF: ReFluxified targets a different BuildCraft implementation; BCCE already provides native FE compatibility." + ordering="NONE" + side="BOTH" + [[dependencies.buildcraftlib]] modId="jei" type="optional" @@ -324,7 +331,7 @@ Robots, docking stations, redstone boards, zones and robotic automation. modId="buildcraftcompat" version="${mod_version}" displayName="BuildCraft Community Edition - Compat" - credits="Special thanks to the original BuildCraft team and all contributors." + credits="Special thanks to the original BuildCraft and Forestry teams and all contributors." authors="CurativeTree, ShipovskijKorp" description=''' Optional cross-mod integration hooks for BuildCraft Community Edition. Integrations without a compatible NeoForge 1.21.1 dependency remain disabled. diff --git a/version-src/1.20.1-forge/src/main/java/buildcraft/lib/BCLib.java b/version-src/1.20.1-forge/src/main/java/buildcraft/lib/BCLib.java index 6eb6174d3..7b9df030e 100644 --- a/version-src/1.20.1-forge/src/main/java/buildcraft/lib/BCLib.java +++ b/version-src/1.20.1-forge/src/main/java/buildcraft/lib/BCLib.java @@ -4,6 +4,8 @@ * distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. */ package buildcraft.lib; +import java.util.List; + import buildcraft.lib.platform.registry.RegistryBinding; import buildcraft.api.v2.BuildCraftApi; import buildcraft.api.v2.BuildCraftServices; @@ -25,6 +27,8 @@ import net.minecraftforge.data.event.GatherDataEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.ModLoadingException; +import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; @@ -41,9 +45,14 @@ public class BCLib { public static final String GIT_COMMIT_MSG = BuildCraftTarget.GIT_COMMIT_MESSAGE; public static final String GIT_COMMIT_AUTHOR = BuildCraftTarget.GIT_COMMIT_AUTHOR; + private static final List INCOMPATIBLE_MODS = List.of( + "buildcraftrf" + ); + public static final boolean DEV = !FMLEnvironment.production || Boolean.getBoolean("buildcraft.dev"); public BCLib() { + rejectIncompatibleMods(); MjApi2PlatformBridge.install(); PlatformApi2Bootstrap.install(); IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); @@ -99,6 +108,24 @@ public BCLib() { } + private static void rejectIncompatibleMods() { + for (String modId : INCOMPATIBLE_MODS) { + if (!ModList.get().isLoaded(modId)) { + continue; + } + String displayName = ModList.get().getModContainerById(modId) + .map(container -> container.getModInfo().getDisplayName()) + .orElse(modId); + throw new ModLoadingException( + ModList.get().getModContainerById(MODID).orElseThrow().getModInfo(), + ModLoadingStage.CONSTRUCT, + "BuildCraft Community Edition is incompatible with " + displayName + " (" + modId + "). " + + "Remove it before starting the game. BCCE already provides native FE compatibility.", + new IllegalStateException("Incompatible mod detected: " + modId) + ); + } + } + public void gatherData(GatherDataEvent event) { var output = event.getGenerator().getPackOutput(); var lookupProvider = event.getLookupProvider(); From 9b52d430503278868c6c78809403b9d0d9be37df Mon Sep 17 00:00:00 2001 From: shipovskijkorp Date: Fri, 2 Oct 2026 12:49:04 +0300 Subject: [PATCH 2/6] auto-test fix --- scripts/validate-fe-compat.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/validate-fe-compat.py b/scripts/validate-fe-compat.py index 4972c2d7a..fc18f5eff 100644 --- a/scripts/validate-fe-compat.py +++ b/scripts/validate-fe-compat.py @@ -4,7 +4,6 @@ from pathlib import Path import json -import re import sys ROOT = Path(__file__).resolve().parents[1] @@ -372,12 +371,15 @@ def require_any(path: str, *needles: str) -> None: require("README.md", "- [x] Add Forge Energy (FE) compatibility") -# Public/new naming is FE. The only old _rf strings permitted are persistence aliases. +# Public/new implementation identifiers use FE. Do not reject plain-language RF mentions: +# external mod names, compatibility diagnostics and historical comments may legitimately contain them. +# Only legacy identifier forms are guarded here; placed-pipe/action aliases remain explicitly allowlisted. allowed_rf_files = { ROOT / "source-families/legacy/src/main/java/buildcraft/transport/BCTransportPipes.java", ROOT / "source-families/modern/src/main/java/buildcraft/transport/BCTransportPipes.java", ROOT / "source-shared/src/main/java/buildcraft/transport/statements/ActionPowerLimit.java", } +legacy_rf_identifiers = ("rf_battery", "rf_limit") for base in (ROOT / "source-shared", ROOT / "source-families", ROOT / "source-platforms", ROOT / "source-family-platforms", ROOT / "version-src"): for p in base.rglob("*"): if not p.is_file() or "build" in p.parts: @@ -386,8 +388,9 @@ def require_any(path: str, *needles: str) -> None: data = p.read_text(encoding="utf-8") except UnicodeDecodeError: continue - if re.search(r"\bRF\b|Redstone Flux|redstone flux|rf_battery|rf_limit", data): - fail(f"user-facing/implementation RF branding remains: {p.relative_to(ROOT)}") + for identifier in legacy_rf_identifiers: + if identifier in data: + fail(f"unexpected legacy RF identifier {identifier!r}: {p.relative_to(ROOT)}") if "_rf" in data and p not in allowed_rf_files: fail(f"unexpected old _rf identifier outside migration aliases: {p.relative_to(ROOT)}") From 92e4fadb54e1d594a42a3362e41cdd4b8a0eca4d Mon Sep 17 00:00:00 2001 From: Smail <44433524+Smail@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:41:29 +0200 Subject: [PATCH 3/6] Make gradlew and build script executable on unix --- build-all.sh | 0 builds/legacy/gradlew | 0 builds/modern/gradlew | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build-all.sh mode change 100644 => 100755 builds/legacy/gradlew mode change 100644 => 100755 builds/modern/gradlew diff --git a/build-all.sh b/build-all.sh old mode 100644 new mode 100755 diff --git a/builds/legacy/gradlew b/builds/legacy/gradlew old mode 100644 new mode 100755 diff --git a/builds/modern/gradlew b/builds/modern/gradlew old mode 100644 new mode 100755 From 8ce1794fce65ce7641a43cabf35b327279bac55b Mon Sep 17 00:00:00 2001 From: Smail <44433524+Smail@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:32:48 +0200 Subject: [PATCH 4/6] [1.19.2-1.21.11] Rotate quad face labels with pluggable geometry --- .../buildcraft/lib/misc/DirectionCompat.java | 13 +++++++ .../lib/client/model/MutableQuad.java | 36 +++++++++++++++++++ .../buildcraft/lib/misc/DirectionCompat.java | 13 +++++++ .../lib/client/model/MutableQuad.java | 36 +++++++++++++++++++ .../buildcraft/lib/misc/DirectionCompat.java | 13 +++++++ 5 files changed, 111 insertions(+) create mode 100644 source-downports/modern/1.21.1/family/src/main/java/buildcraft/lib/misc/DirectionCompat.java create mode 100644 source-families/legacy/src/main/java/buildcraft/lib/misc/DirectionCompat.java create mode 100644 source-families/modern/src/main/java/buildcraft/lib/misc/DirectionCompat.java diff --git a/source-downports/modern/1.21.1/family/src/main/java/buildcraft/lib/misc/DirectionCompat.java b/source-downports/modern/1.21.1/family/src/main/java/buildcraft/lib/misc/DirectionCompat.java new file mode 100644 index 000000000..c1f9d87f9 --- /dev/null +++ b/source-downports/modern/1.21.1/family/src/main/java/buildcraft/lib/misc/DirectionCompat.java @@ -0,0 +1,13 @@ +package buildcraft.lib.misc; + +import net.minecraft.core.Direction; + +/** Direction lookups whose Minecraft API differs between versions. */ +public final class DirectionCompat { + private DirectionCompat() {} + + /** @return The direction closest to the given vector. */ + public static Direction nearest(float x, float y, float z) { + return Direction.getNearest(x, y, z); + } +} diff --git a/source-families/legacy/src/main/java/buildcraft/lib/client/model/MutableQuad.java b/source-families/legacy/src/main/java/buildcraft/lib/client/model/MutableQuad.java index 8bcc05a15..d64071fa6 100644 --- a/source-families/legacy/src/main/java/buildcraft/lib/client/model/MutableQuad.java +++ b/source-families/legacy/src/main/java/buildcraft/lib/client/model/MutableQuad.java @@ -28,6 +28,8 @@ import net.minecraft.core.Vec3i; import net.minecraft.world.phys.Vec3; +import buildcraft.lib.misc.DirectionCompat; + /** Holds all of the information necessary to make a {@link BakedQuad}. This provides a variety of methods to quickly * set or get different elements. This currently holds 4 {@link MutableVertex}. */ public class MutableQuad { @@ -705,9 +707,43 @@ public MutableQuad rotate(Direction from, Direction to, float ox, float oy, floa } // @formatter:on translatef(ox, oy, oz); + if (face != null) { + face = rotateFace(face, from, to); + } return this; } + /** Rotates a face label the same way {@link #rotate(Direction, Direction, float, float, float)} rotates geometry, + * by rotating a probe quad whose vertices sit one block step from the origin along the face. Without this a + * rotated quad keeps its old culling and shading direction, so baked pluggables look different (or get culled) per + * viewing direction. */ + private static Direction rotateFace(Direction face, Direction from, Direction to) { + return ROTATED_FACES[(from.ordinal() * 6 + to.ordinal()) * 6 + face.ordinal()]; + } + + /** Every (from, to, face) result of {@link #probeRotatedFace}, so rotating a quad does not allocate. */ + private static final Direction[] ROTATED_FACES = new Direction[6 * 6 * 6]; + + static { + for (Direction from : Direction.values()) { + for (Direction to : Direction.values()) { + for (Direction face : Direction.values()) { + ROTATED_FACES[(from.ordinal() * 6 + to.ordinal()) * 6 + face.ordinal()] = probeRotatedFace(face, from, to); + } + } + } + } + + private static Direction probeRotatedFace(Direction face, Direction from, Direction to) { + MutableQuad probe = new MutableQuad(); + for (MutableVertex vertex : probe.vertexs) { + vertex.positionf(face.getStepX(), face.getStepY(), face.getStepZ()); + } + probe.rotate(from, to, 0, 0, 0); + MutableVertex moved = probe.vertex_0; + return DirectionCompat.nearest(moved.position_x, moved.position_y, moved.position_z); + } + public MutableQuad rotateX_90(float scale) { vertex_0.rotateX_90(scale); vertex_1.rotateX_90(scale); diff --git a/source-families/legacy/src/main/java/buildcraft/lib/misc/DirectionCompat.java b/source-families/legacy/src/main/java/buildcraft/lib/misc/DirectionCompat.java new file mode 100644 index 000000000..c1f9d87f9 --- /dev/null +++ b/source-families/legacy/src/main/java/buildcraft/lib/misc/DirectionCompat.java @@ -0,0 +1,13 @@ +package buildcraft.lib.misc; + +import net.minecraft.core.Direction; + +/** Direction lookups whose Minecraft API differs between versions. */ +public final class DirectionCompat { + private DirectionCompat() {} + + /** @return The direction closest to the given vector. */ + public static Direction nearest(float x, float y, float z) { + return Direction.getNearest(x, y, z); + } +} diff --git a/source-families/modern/src/main/java/buildcraft/lib/client/model/MutableQuad.java b/source-families/modern/src/main/java/buildcraft/lib/client/model/MutableQuad.java index d2c76ea2e..c243eab14 100644 --- a/source-families/modern/src/main/java/buildcraft/lib/client/model/MutableQuad.java +++ b/source-families/modern/src/main/java/buildcraft/lib/client/model/MutableQuad.java @@ -19,6 +19,8 @@ import net.minecraft.core.Vec3i; import net.minecraft.world.phys.Vec3; +import buildcraft.lib.misc.DirectionCompat; + /** Holds all of the information necessary to make a {@link BakedQuad}. This provides a variety of methods to quickly * set or get different elements. This currently holds 4 {@link MutableVertex}. */ public class MutableQuad { @@ -684,9 +686,43 @@ public MutableQuad rotate(Direction from, Direction to, float ox, float oy, floa } // @formatter:on translatef(ox, oy, oz); + if (face != null) { + face = rotateFace(face, from, to); + } return this; } + /** Rotates a face label the same way {@link #rotate(Direction, Direction, float, float, float)} rotates geometry, + * by rotating a probe quad whose vertices sit one block step from the origin along the face. Without this a + * rotated quad keeps its old culling and shading direction, so baked pluggables look different (or get culled) per + * viewing direction. */ + private static Direction rotateFace(Direction face, Direction from, Direction to) { + return ROTATED_FACES[(from.ordinal() * 6 + to.ordinal()) * 6 + face.ordinal()]; + } + + /** Every (from, to, face) result of {@link #probeRotatedFace}, so rotating a quad does not allocate. */ + private static final Direction[] ROTATED_FACES = new Direction[6 * 6 * 6]; + + static { + for (Direction from : Direction.values()) { + for (Direction to : Direction.values()) { + for (Direction face : Direction.values()) { + ROTATED_FACES[(from.ordinal() * 6 + to.ordinal()) * 6 + face.ordinal()] = probeRotatedFace(face, from, to); + } + } + } + } + + private static Direction probeRotatedFace(Direction face, Direction from, Direction to) { + MutableQuad probe = new MutableQuad(); + for (MutableVertex vertex : probe.vertexs) { + vertex.positionf(face.getStepX(), face.getStepY(), face.getStepZ()); + } + probe.rotate(from, to, 0, 0, 0); + MutableVertex moved = probe.vertex_0; + return DirectionCompat.nearest(moved.position_x, moved.position_y, moved.position_z); + } + public MutableQuad rotateX_90(float scale) { vertex_0.rotateX_90(scale); vertex_1.rotateX_90(scale); diff --git a/source-families/modern/src/main/java/buildcraft/lib/misc/DirectionCompat.java b/source-families/modern/src/main/java/buildcraft/lib/misc/DirectionCompat.java new file mode 100644 index 000000000..7f6750643 --- /dev/null +++ b/source-families/modern/src/main/java/buildcraft/lib/misc/DirectionCompat.java @@ -0,0 +1,13 @@ +package buildcraft.lib.misc; + +import net.minecraft.core.Direction; + +/** Direction lookups whose Minecraft API differs between versions. */ +public final class DirectionCompat { + private DirectionCompat() {} + + /** @return The direction closest to the given vector. */ + public static Direction nearest(float x, float y, float z) { + return Direction.getApproximateNearest(x, y, z); + } +} From adb90e86b770f4c6d57349906afce5d4a154fb8f Mon Sep 17 00:00:00 2001 From: Smail <44433524+Smail@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:32:51 +0200 Subject: [PATCH 5/6] [1.19.2-1.21.11] Fix the flipped north face texture of the blocker plug --- .../assets/buildcrafttransport/models/plugs/blocker.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source-shared/src/main/resources/assets/buildcrafttransport/models/plugs/blocker.json b/source-shared/src/main/resources/assets/buildcrafttransport/models/plugs/blocker.json index 716907afd..9b3177446 100644 --- a/source-shared/src/main/resources/assets/buildcrafttransport/models/plugs/blocker.json +++ b/source-shared/src/main/resources/assets/buildcrafttransport/models/plugs/blocker.json @@ -44,7 +44,7 @@ "faces": { "down": { "uv": [ 2, 4, 4, 12 ], "texture": "#all" }, "up": { "uv": [ 2, 4, 4, 12 ], "texture": "#all" }, - "north": { "uv": [ 2, 4, 4, 12 ], "texture": "#all" }, + "north": { "uv": [ 4, 4, 2, 12 ], "texture": "#all" }, "south": { "uv": [ 2, 4, 4, 12 ], "texture": "#all" }, "west": { "uv": [ 4, 4, 12, 12 ], "texture": "#all" }, "east": { "uv": [ 4, 4, 12, 12 ], "texture": "#all" } From 186d290b9e850448a1f58b02e7f08ef76afb6521 Mon Sep 17 00:00:00 2001 From: Smail <44433524+Smail@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:44:11 +0200 Subject: [PATCH 6/6] [1.19.2-1.21.11] Test that rotating a quad rotates its face label --- .../client/model/MutableQuadRotateTester.java | 63 +++++++++++++++++++ .../client/model/MutableQuadRotateTester.java | 63 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 source-families/legacy/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java create mode 100644 source-families/modern/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java diff --git a/source-families/legacy/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java b/source-families/legacy/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java new file mode 100644 index 000000000..301cfc7a2 --- /dev/null +++ b/source-families/legacy/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java @@ -0,0 +1,63 @@ +package buildcraft.lib.client.model; + +import net.minecraft.core.Direction; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import buildcraft.lib.misc.DirectionCompat; + +class MutableQuadRotateTester { + /** A rotated quad must keep a face label that matches the block side its geometry now lies on. */ + @Test + void rotatedQuadFaceLabelMatchesGeometry() { + for (Direction face : Direction.values()) { + for (Direction from : Direction.values()) { + for (Direction to : Direction.values()) { + MutableQuad quad = quadOnFace(face); + quad.rotate(from, to, 0.5f, 0.5f, 0.5f); + Assertions.assertEquals(sideOf(quad), quad.getFace(), + "face " + face + " rotated " + from + " -> " + to); + } + } + } + } + + /** A quad without a face label must stay without one. */ + @Test + void rotatedQuadWithoutFaceStaysUnlabelled() { + MutableQuad quad = quadOnFace(Direction.UP); + quad.setFace(null); + quad.rotate(Direction.UP, Direction.NORTH, 0.5f, 0.5f, 0.5f); + Assertions.assertNull(quad.getFace()); + } + + /** A unit square lying on the given side of the unit block. */ + private static MutableQuad quadOnFace(Direction face) { + MutableQuad quad = new MutableQuad(-1, face); + int axis = face.getAxis().ordinal(); + int a = (axis + 1) % 3; + int b = (axis + 2) % 3; + float plane = face.getAxisDirection() == Direction.AxisDirection.POSITIVE ? 1 : 0; + float[][] corners = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } }; + for (int i = 0; i < 4; i++) { + float[] pos = new float[3]; + pos[axis] = plane; + pos[a] = corners[i][0]; + pos[b] = corners[i][1]; + quad.vertexs[i].positionf(pos[0], pos[1], pos[2]); + } + return quad; + } + + /** The block side the quad lies on: its centre relative to the block centre, independent of the stored label. */ + private static Direction sideOf(MutableQuad quad) { + float x = 0, y = 0, z = 0; + for (MutableVertex vertex : quad.vertexs) { + x += vertex.position_x / 4 - 0.125f; + y += vertex.position_y / 4 - 0.125f; + z += vertex.position_z / 4 - 0.125f; + } + return DirectionCompat.nearest(x, y, z); + } +} diff --git a/source-families/modern/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java b/source-families/modern/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java new file mode 100644 index 000000000..301cfc7a2 --- /dev/null +++ b/source-families/modern/src/test/java/buildcraft/lib/client/model/MutableQuadRotateTester.java @@ -0,0 +1,63 @@ +package buildcraft.lib.client.model; + +import net.minecraft.core.Direction; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import buildcraft.lib.misc.DirectionCompat; + +class MutableQuadRotateTester { + /** A rotated quad must keep a face label that matches the block side its geometry now lies on. */ + @Test + void rotatedQuadFaceLabelMatchesGeometry() { + for (Direction face : Direction.values()) { + for (Direction from : Direction.values()) { + for (Direction to : Direction.values()) { + MutableQuad quad = quadOnFace(face); + quad.rotate(from, to, 0.5f, 0.5f, 0.5f); + Assertions.assertEquals(sideOf(quad), quad.getFace(), + "face " + face + " rotated " + from + " -> " + to); + } + } + } + } + + /** A quad without a face label must stay without one. */ + @Test + void rotatedQuadWithoutFaceStaysUnlabelled() { + MutableQuad quad = quadOnFace(Direction.UP); + quad.setFace(null); + quad.rotate(Direction.UP, Direction.NORTH, 0.5f, 0.5f, 0.5f); + Assertions.assertNull(quad.getFace()); + } + + /** A unit square lying on the given side of the unit block. */ + private static MutableQuad quadOnFace(Direction face) { + MutableQuad quad = new MutableQuad(-1, face); + int axis = face.getAxis().ordinal(); + int a = (axis + 1) % 3; + int b = (axis + 2) % 3; + float plane = face.getAxisDirection() == Direction.AxisDirection.POSITIVE ? 1 : 0; + float[][] corners = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } }; + for (int i = 0; i < 4; i++) { + float[] pos = new float[3]; + pos[axis] = plane; + pos[a] = corners[i][0]; + pos[b] = corners[i][1]; + quad.vertexs[i].positionf(pos[0], pos[1], pos[2]); + } + return quad; + } + + /** The block side the quad lies on: its centre relative to the block centre, independent of the stored label. */ + private static Direction sideOf(MutableQuad quad) { + float x = 0, y = 0, z = 0; + for (MutableVertex vertex : quad.vertexs) { + x += vertex.position_x / 4 - 0.125f; + y += vertex.position_y / 4 - 0.125f; + z += vertex.position_z / 4 - 0.125f; + } + return DirectionCompat.nearest(x, y, z); + } +}