From 90ed0b24e72f8660ad95ccc6b24aa571b6a24305 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 22 Jul 2023 13:49:26 +0100 Subject: [PATCH] Send block updates to the computer when updating redstone Fixes #1520 --- .../blocks/AbstractComputerBlockEntity.java | 15 +- .../shared/util/RedstoneUtil.java | 4 +- .../computercraft/gametest/Computer_Test.kt | 19 +++ .../computer_test.self_output_update.snbt | 137 ++++++++++++++++++ 4 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/computer_test.self_output_update.snbt diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java index d72c34c51..17b85086a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java @@ -199,6 +199,11 @@ protected ComputerSide remapLocalSide(ComputerSide localSide) { return localSide; } + private void updateRedstoneInputs(ServerComputer computer) { + var pos = getBlockPos(); + for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, pos.relative(dir)); + } + private void updateRedstoneInput(ServerComputer computer, Direction dir, BlockPos targetPos) { var offsetSide = dir.getOpposite(); var localDir = remapToLocalSide(dir); @@ -254,8 +259,7 @@ private void updateInputAt(BlockPos neighbour) { // If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods // handle this incorrectly. - var pos = getBlockPos(); - for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, pos.relative(dir)); + updateRedstoneInputs(computer); invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty. } @@ -264,9 +268,10 @@ private void updateInputAt(BlockPos neighbour) { */ public void updateOutput() { BlockEntityHelpers.updateBlock(this); - for (var dir : DirectionUtil.FACINGS) { - RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), dir); - } + for (var dir : DirectionUtil.FACINGS) RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), dir); + + var computer = getServerComputer(); + if (computer != null) updateRedstoneInputs(computer); } protected abstract ServerComputer createComputer(int id); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java index e772c8806..457fb444b 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java @@ -50,6 +50,8 @@ public static void propagateRedstoneOutput(Level world, BlockPos pos, Direction var neighbourPos = pos.relative(side); world.neighborChanged(neighbourPos, block.getBlock(), pos); - world.updateNeighborsAtExceptFromFacing(neighbourPos, block.getBlock(), side.getOpposite()); + // We intentionally use updateNeighborsAt here instead of updateNeighborsAtExceptFromFacing, as computers can + // both send and receive redstone, and so also need to be updated. + world.updateNeighborsAt(neighbourPos, block.getBlock()); } } diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt index 19f8279c1..f7800b2b5 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.lwjgl.glfw.GLFW +import kotlin.time.Duration.Companion.milliseconds class Computer_Test { /** @@ -71,6 +72,24 @@ fun Set_and_destroy(context: GameTestHelper) = context.sequence { thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") } } + /** + * Check computers pick up propagated redstone to surrounding blocks. + * + * @see [#1520](https://github.com/cc-tweaked/CC-Tweaked/issues/1520) + */ + @GameTest + fun Self_output_update(context: GameTestHelper) = context.sequence { + thenOnComputer { + getApi().setOutput(ComputerSide.BACK, true) + sleep(100.milliseconds) + assertEquals(true, getApi().getInput(ComputerSide.BACK), "Input should be on") + + getApi().setOutput(ComputerSide.BACK, false) + sleep(100.milliseconds) + assertEquals(false, getApi().getInput(ComputerSide.BACK), "Input should be off") + } + } + /** * Check computers and turtles expose peripherals. */ diff --git a/projects/common/src/testMod/resources/data/cctest/structures/computer_test.self_output_update.snbt b/projects/common/src/testMod/resources/data/cctest/structures/computer_test.self_output_update.snbt new file mode 100644 index 000000000..d0dab68cf --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/computer_test.self_output_update.snbt @@ -0,0 +1,137 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "computer_test.self_output_update", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [2, 1, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:air", + "computercraft:computer_advanced{facing:north,state:on}" + ] +}