From f79d559fd24aac75228740110652d5b3a5c77373 Mon Sep 17 00:00:00 2001 From: MrTJP Date: Tue, 31 Mar 2026 15:28:25 -0400 Subject: [PATCH 1/4] build: fix crash when starting from client/server from gradle Use api project as compileOnly to prevent double classes due to JarJar. This prevents a module conflict crash. --- core/build.gradle | 3 ++- expansion/build.gradle | 1 + exploration/build.gradle | 1 + fabrication/build.gradle | 1 + illumination/build.gradle | 1 + integration/build.gradle | 1 + runtime/build.gradle | 10 ++++++---- transmission/build.gradle | 1 + 8 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2d8d6fa6d..98ead9f95 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -31,7 +31,8 @@ dependencies { accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" // ProjectRed API - jarJar(api project(":api")) // Use JarJar to bake within Core jar + compileOnly project(":api") // Not on runtime bc already included via JarJar + jarJar(project(":api")) // Use JarJar to bake within Core jar // JEI compileOnly("mezz.jei:jei-${mc_version}-common-api:${jei_version}") diff --git a/expansion/build.gradle b/expansion/build.gradle index 9cb64487f..9d6967fe4 100644 --- a/expansion/build.gradle +++ b/expansion/build.gradle @@ -24,5 +24,6 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") } diff --git a/exploration/build.gradle b/exploration/build.gradle index 8575f37b0..43e6e727d 100644 --- a/exploration/build.gradle +++ b/exploration/build.gradle @@ -24,5 +24,6 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") } diff --git a/fabrication/build.gradle b/fabrication/build.gradle index 83420b415..efe54bd75 100644 --- a/fabrication/build.gradle +++ b/fabrication/build.gradle @@ -24,6 +24,7 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") implementation project(":integration") implementation project(":transmission") diff --git a/illumination/build.gradle b/illumination/build.gradle index ac3affe13..46ffcac35 100644 --- a/illumination/build.gradle +++ b/illumination/build.gradle @@ -24,5 +24,6 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") } diff --git a/integration/build.gradle b/integration/build.gradle index 7a18b79ce..cfa350726 100644 --- a/integration/build.gradle +++ b/integration/build.gradle @@ -24,5 +24,6 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") } diff --git a/runtime/build.gradle b/runtime/build.gradle index cf6ad6efc..0d98000c0 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -11,13 +11,16 @@ neoForge { accessTransformers.from "../core/src/main/resources/META-INF/accesstransformer.cfg" runs { - configureEach { - } - client { + client() } server { + server() + } + + configureEach { + logLevel = org.slf4j.event.Level.DEBUG } } } @@ -31,4 +34,3 @@ dependencies { runtimeOnly project(":integration") runtimeOnly project(":transmission") } - diff --git a/transmission/build.gradle b/transmission/build.gradle index 7a31536cb..72eed0479 100644 --- a/transmission/build.gradle +++ b/transmission/build.gradle @@ -24,5 +24,6 @@ dependencies { accessTransformers "io.codechicken:CodeChickenLib:${mc_version}-${ccl_version}" accessTransformers "io.codechicken:CBMultipart:${mc_version}-${cbm_version}" + compileOnly project(":api") implementation project(":core") } From f3a4c3299fe3a572eb5f94ad748d7438ce858b95 Mon Sep 17 00:00:00 2001 From: MrTJP Date: Tue, 31 Mar 2026 15:48:26 -0400 Subject: [PATCH 2/4] fix: wires not propagating on inner corners if on connectable block --- .../mrtjp/projectred/integration/part/ArrayGatePart.java | 8 ++++---- .../mrtjp/projectred/transmission/part/RedwirePart.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/ArrayGatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/ArrayGatePart.java index 2b938aae0..8c422a845 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/ArrayGatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/ArrayGatePart.java @@ -124,8 +124,8 @@ public int calculateSignal() { if ((propagationMask & 1 << r) == 0) { continue; } int s; - if (maskConnectsCorner(r)) { - FaceLookup lookup = FaceLookup.lookupCorner(level(), pos(), getSide(), r); + if (maskConnectsInside(r)) { + FaceLookup lookup = FaceLookup.lookupInsideFace(level(), pos(), getSide(), r); s = RedstoneFaceLookup.resolveSignal(lookup, true); } else if (maskConnectsStraight(r)) { @@ -135,8 +135,8 @@ public int calculateSignal() { s = RedstoneFaceLookup.resolveVanillaSignal(lookup, this, true, true); } - } else if (maskConnectsInside(r)) { - FaceLookup lookup = FaceLookup.lookupInsideFace(level(), pos(), getSide(), r); + } else if (maskConnectsCorner(r)) { + FaceLookup lookup = FaceLookup.lookupCorner(level(), pos(), getSide(), r); s = RedstoneFaceLookup.resolveSignal(lookup, true); } else { // For non-connected sides, just do a vanilla signal lookup diff --git a/transmission/src/main/java/mrtjp/projectred/transmission/part/RedwirePart.java b/transmission/src/main/java/mrtjp/projectred/transmission/part/RedwirePart.java index 157b83004..c5a50b500 100644 --- a/transmission/src/main/java/mrtjp/projectred/transmission/part/RedwirePart.java +++ b/transmission/src/main/java/mrtjp/projectred/transmission/part/RedwirePart.java @@ -170,8 +170,8 @@ public int calculateSignal() { for (int r = 0; r < 4; r++) { int s = 0; - if (maskConnectsCorner(r)) { - FaceLookup lookup = FaceLookup.lookupCorner(level(), pos(), getSide(), r); + if (maskConnectsInside(r)) { + FaceLookup lookup = FaceLookup.lookupInsideFace(level(), pos(), getSide(), r); s = resolveSignal(lookup); } else if (maskConnectsStraight(r)) { @@ -181,8 +181,8 @@ public int calculateSignal() { s = RedstoneFaceLookup.resolveVanillaSignal(lookup, this, true, true); } - } else if (maskConnectsInside(r)) { - FaceLookup lookup = FaceLookup.lookupInsideFace(level(), pos(), getSide(), r); + } else if (maskConnectsCorner(r)) { + FaceLookup lookup = FaceLookup.lookupCorner(level(), pos(), getSide(), r); s = resolveSignal(lookup); } From 6c0756586924904b83761d5394473536fbff9098 Mon Sep 17 00:00:00 2001 From: MrTJP Date: Tue, 31 Mar 2026 15:51:39 -0400 Subject: [PATCH 3/4] test: add transmission game tests --- transmission/build.gradle | 17 +++++ .../gametests/RedwirePropagationTests.java | 68 ++++++++++++++++++ .../alloy_rw_wraparound_rs_block.nbt | Bin 0 -> 542 bytes .../insulated_rw_wraparound_rs_block.nbt | Bin 0 -> 551 bytes 4 files changed, 85 insertions(+) create mode 100644 transmission/src/main/java/mrtjp/projectred/transmission/gametests/RedwirePropagationTests.java create mode 100644 transmission/src/main/resources/data/projectred_transmission/structure/alloy_rw_wraparound_rs_block.nbt create mode 100644 transmission/src/main/resources/data/projectred_transmission/structure/insulated_rw_wraparound_rs_block.nbt diff --git a/transmission/build.gradle b/transmission/build.gradle index 72eed0479..938fb6d5d 100644 --- a/transmission/build.gradle +++ b/transmission/build.gradle @@ -14,6 +14,23 @@ neoForge { data() programArguments.addAll '--mod', mod_id, '--all', '--output', file("src/main/generated").absolutePath, '--existing', file("src/main/resources").absolutePath } + + gameTestServer { + type = "gameTestServer" + gameDirectory = project.file('runs/gameTestServer') + systemProperty 'neoforge.enableGameTest', 'true' + systemProperty 'neoforge.enabledGameTestNamespaces', mod_id + } + + configureEach { + logLevel = org.slf4j.event.Level.DEBUG + } + } + + mods { + "${mod_id}" { + sourceSet(sourceSets.main) + } } } diff --git a/transmission/src/main/java/mrtjp/projectred/transmission/gametests/RedwirePropagationTests.java b/transmission/src/main/java/mrtjp/projectred/transmission/gametests/RedwirePropagationTests.java new file mode 100644 index 000000000..ad99fbb63 --- /dev/null +++ b/transmission/src/main/java/mrtjp/projectred/transmission/gametests/RedwirePropagationTests.java @@ -0,0 +1,68 @@ +package mrtjp.projectred.transmission.gametests; + +import mrtjp.projectred.transmission.ProjectRedTransmission; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.level.block.LeverBlock; +import net.minecraft.world.level.block.RedstoneLampBlock; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; + +@GameTestHolder(ProjectRedTransmission.MOD_ID) +@PrefixGameTestTemplate(false) +public class RedwirePropagationTests { + + @GameTest(template = "alloy_rw_wraparound_rs_block") + public static void redAlloyPropagatesAroundRSBlock(GameTestHelper helper) { + // Red alloy wires should propagate signal around a redsone block, + // and also power the lamp underneath + + // Positions + var lever = new BlockPos(0, 2, 1); + var lampA = new BlockPos(3, 2, 1); + var lampB = new BlockPos(6, 2, 1); + + // Assert start condition + helper.assertBlockState(lever, state -> !state.getValue(LeverBlock.POWERED), () -> "Lever should be off!"); + helper.assertBlockState(lampA, state -> !state.getValue(RedstoneLampBlock.LIT), () -> "Lamp should be off!"); + helper.assertBlockState(lampB, state -> !state.getValue(RedstoneLampBlock.LIT), () -> "Lamp should be off!"); + + // Pull lever, wait 2 ticks, then check lamp states + helper.startSequence() + .thenExecute(() -> helper.setBlock(lever, helper.getBlockState(lever).setValue(LeverBlock.POWERED, true))) + .thenIdle(2) + .thenExecute(() -> { + helper.assertBlockState(lever, state -> state.getValue(LeverBlock.POWERED), () -> "Lever should be on!"); + helper.assertBlockState(lampA, state -> state.getValue(RedstoneLampBlock.LIT), () -> "Lamp A should be on!"); + helper.assertBlockState(lampB, state -> state.getValue(RedstoneLampBlock.LIT), () -> "Lamp B should be on!"); + }).thenSucceed(); + } + + @GameTest(template = "insulated_rw_wraparound_rs_block") + public static void insulatedWirePropagatesAroundRSBlock(GameTestHelper helper) { + // Insulated wires should propagate signal around a redsone block, + // but NOT power the lamp underneath + + // Positions + var lever = new BlockPos(0, 2, 1); + var lampA = new BlockPos(3, 2, 1); + var lampB = new BlockPos(6, 2, 1); + + // Assert start condition + helper.assertBlockState(lever, state -> !state.getValue(LeverBlock.POWERED), () -> "Lever should be off!"); + helper.assertBlockState(lampA, state -> !state.getValue(RedstoneLampBlock.LIT), () -> "Lamp should be off!"); + helper.assertBlockState(lampB, state -> !state.getValue(RedstoneLampBlock.LIT), () -> "Lamp should be off!"); + + // Pull lever, wait 2 ticks, then check lamp states + helper.startSequence() + .thenExecute(() -> helper.setBlock(lever, helper.getBlockState(lever).setValue(LeverBlock.POWERED, true))) + .thenIdle(2) + .thenExecute(() -> { + helper.assertBlockState(lever, state -> state.getValue(LeverBlock.POWERED), () -> "Lever should be on!"); + helper.assertBlockState(lampA, state -> !state.getValue(RedstoneLampBlock.LIT), () -> "Lamp A should be off!"); + helper.assertBlockState(lampB, state -> state.getValue(RedstoneLampBlock.LIT), () -> "Lamp B should be on!"); + }).thenSucceed(); + } + +} diff --git a/transmission/src/main/resources/data/projectred_transmission/structure/alloy_rw_wraparound_rs_block.nbt b/transmission/src/main/resources/data/projectred_transmission/structure/alloy_rw_wraparound_rs_block.nbt new file mode 100644 index 0000000000000000000000000000000000000000..53659b5f25632f34be49db13860545ad3bc0baf8 GIT binary patch literal 542 zcmV+(0^$81iwFP!00000|HYTfZWA#ShL5kA2|`^Ul@04YL^o{Mf>^PQoQYFR9eZS( zR`4`^#0s%s$xN9{(kho za7n-=0ha_^3UDdFr2v-(TpDm`z_s3?Z4TSPIhLUzv)wb_liB8oJsEz0!vGfpI2_<` zfFl5o05}rhNPwdNjsiFu;AntD!#N(?4gO>|_GOOk#xWNITnun=z{LR<2V4Sh34kL3 zjs$v^fJ*@`1-KO8(tt|?F1^z=B@yPOCe@l$9v&SS>mN84wkw)6F@%q%CAz<>h`R+{ zv?+eGO-Xh!E6Iesayh#gF8eQx_wmQ<)+NhGXLl8hC;C_Su9?qP-+ght<}#a`EXbN)Hdxmd%m&xDP#XSb!_=kV@q)C7hg+tLZf&0)8}h+T&72j z)--92x24#=NmY^jWLB2mtX!~7GRvMC7P-V=yP>9^Q*3SO|6Eigyep)Z#%D|Gy0@FU zuJwnqnK!F;47}b1j7v|Ea%mddF@21w>N<Vqlfi%tC@6P;!H3+(+f2~rIJ0B6tswEzGB literal 0 HcmV?d00001 diff --git a/transmission/src/main/resources/data/projectred_transmission/structure/insulated_rw_wraparound_rs_block.nbt b/transmission/src/main/resources/data/projectred_transmission/structure/insulated_rw_wraparound_rs_block.nbt new file mode 100644 index 0000000000000000000000000000000000000000..e93b9dd7bbc8cc61276f7fabf97f2c7fdac3123e GIT binary patch literal 551 zcmV+?0@(c@iwFP!00000|IL@pZqq;%M(_N_4nkcZ!G=fR3A$m!7Q~8eG;t>FfIVZ) zOdE-(=_6K%1xxJWIBDf1A1aEl6h)T5d(NG!tH_xXl_Zn`}I#l1%yt{c{l4wKP7LAu6m9oipsF=zyAj6Mj7~tAwj=31%;(&_-E)KW^;1YmK z04@o*B;b;OO93tgxD?>hfJ*}|4Y;;Dw9jD^oKqPZGW$LA1DSn}IFR8-I1F$xfWrX} z2RH)Y2!JC2js!Rg;3$Bj0geVZG|chTZty3&aVT?YH%_@2;9`J_11=7@IN%b1O8^`R za3s*P1Y8PmDZr%wmj+xKaOp|coP=NIl2mJAet30YtbgHH*{*0-1@AwamgxSjAnsQ5 zu8rZVt#Y)hMUKX&#--$fzua8AY!}X_o4P}<78@5WBc0rpH#O0p>bKQ$(cIM8^|H%k zZoA-kf8b|s|44G8T|U+BuV=P9Fx5WuCp-5D_+O4d{>yf!#`@#zTOh#^{3lDRp3&~z z{rueRj!X8a-I8Xlscp`-Z&ei}KbgkTtHya-MYHayewA|YwmaJVa}2GG{ojy+MC~%E zwW*V(ZQYw)U3a2~vPEs0b_~4S1&oVNk#cP++cE19V$pRLHnzhIl7e*i*!e3`KE!-I pGB0+_+TPkYK6w3TJiEaNdhx+z^Toz~)I=AFegh^A2|;`f008O84(0#= literal 0 HcmV?d00001 From 2b01cc07065743578cfe997bd6dc2dc271f48dd6 Mon Sep 17 00:00:00 2001 From: MrTJP Date: Tue, 31 Mar 2026 16:26:53 -0400 Subject: [PATCH 4/4] ci: run game tests in pull request validation --- .github/workflows/verify-pull-request.yaml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/verify-pull-request.yaml b/.github/workflows/verify-pull-request.yaml index f8db9be48..c039245d7 100644 --- a/.github/workflows/verify-pull-request.yaml +++ b/.github/workflows/verify-pull-request.yaml @@ -106,3 +106,35 @@ jobs: with: name: project-red-${{ steps.versioning.outputs.version }} path: '*/build/libs/ProjectRed-*.jar' + + integration_tests: + name: Integration Test - ${{ matrix.module }} + runs-on: ubuntu-latest + needs: build_test + strategy: + matrix: + module: [transmission] + steps: + - name: Checkout git repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '21' + + - name: Restore Gradle cache + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle2-${{ hashFiles('**/build.gradle', '**/build.properties', '**/gradle.properties', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle2- + + - name: Run game tests + run: ./gradlew :${{ matrix.module }}:runGameTestServer