From 4f66ac79d3774a4093d3d14979c10c2f1d59c24d Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 12 Nov 2024 09:05:27 +0000 Subject: [PATCH] Add redstone relay block (#2002) - Move redstone methods out of the IAPIEnvironment, and into a new RedstoneAccess. We similarly move the implementation from Environment into a new RedstoneState class. The interface is possibly a little redundant (interfaces with a single implementation are always a little suspect), but it's nice to keep the consumer/producer interfaces separate. - Abstract most redstone API methods into a separate shared class, that can be used by both the rs API and the new redstone relay. - Add the new redstone relay block. The docs are probably a little lacking here, but I really struggled to write anything which wasn't just "look, it's the same as the redstone API". --- doc/events/redstone.md | 6 +- gradle/libs.versions.toml | 2 +- .../blockstates/redstone_relay.json | 8 + .../assets/computercraft/lang/en_us.json | 1 + .../models/block/redstone_relay.json | 9 + .../models/item/redstone_relay.json | 1 + .../recipes/redstone/redstone_relay.json | 16 ++ .../loot_tables/blocks/redstone_relay.json | 12 + .../data/BlockModelProvider.java | 14 + .../computercraft/data/LanguageProvider.java | 1 + .../computercraft/data/LootTableProvider.java | 1 + .../computercraft/data/RecipeProvider.java | 11 + .../computercraft/data/TagProvider.java | 5 +- .../computercraft/shared/ModRegistry.java | 17 +- .../blocks/AbstractComputerBlockEntity.java | 8 +- .../shared/computer/core/ServerComputer.java | 16 +- .../redstone/RedstoneRelayBlock.java | 84 ++++++ .../redstone/RedstoneRelayBlockEntity.java | 92 +++++++ .../redstone/RedstoneRelayPeripheral.java | 71 +++++ .../textures/block/redstone_relay_bottom.png | Bin 0 -> 289 bytes .../textures/block/redstone_relay_front.png | Bin 0 -> 334 bytes .../textures/block/redstone_relay_side.png | Bin 0 -> 316 bytes .../textures/block/redstone_relay_top.png | Bin 0 -> 289 bytes .../computercraft/gametest/Relay_Test.kt | 99 +++++++ .../computercraft/gametest/core/TestHooks.kt | 1 + .../relay_test.no_through_signal.snbt | 141 ++++++++++ .../relay_test.no_through_signal_reverse.snbt | 141 ++++++++++ .../relay_test.self_output_update.snbt | 137 ++++++++++ .../relay_test.set_and_destroy.snbt | 138 ++++++++++ .../core/apis/IAPIEnvironment.java | 12 - .../computercraft/core/apis/RedstoneAPI.java | 137 +--------- .../core/apis/RedstoneMethods.java | 147 +++++++++++ .../computercraft/core/computer/Computer.java | 17 +- .../core/computer/ComputerExecutor.java | 7 +- .../core/computer/Environment.java | 139 +--------- .../core/redstone/RedstoneAccess.java | 63 +++++ .../core/redstone/RedstoneState.java | 248 ++++++++++++++++++ .../test/core/apis/BasicApiEnvironment.java | 28 -- .../computercraft/recipes/redstone_relay.json | 12 + .../minecraft/tags/blocks/mineable/axe.json | 1 + .../tags/blocks/mineable/pickaxe.json | 3 +- .../computercraft/shared/ComputerCraft.java | 1 + .../computercraft/recipes/redstone_relay.json | 12 + .../minecraft/tags/blocks/mineable/axe.json | 1 + .../tags/blocks/mineable/pickaxe.json | 3 +- .../shared/ForgeCommonHooks.java | 14 +- .../web/src/htmlTransform/export/index.json | 83 +++++- .../items/computercraft/redstone_relay.png | Bin 0 -> 1625 bytes .../export/items/minecraft/stick.png | Bin 0 -> 154 bytes .../cc/tweaked/web/stub/ReentrantLock.java | 3 + 50 files changed, 1610 insertions(+), 353 deletions(-) create mode 100644 projects/common/src/generated/resources/assets/computercraft/blockstates/redstone_relay.json create mode 100644 projects/common/src/generated/resources/assets/computercraft/models/block/redstone_relay.json create mode 100644 projects/common/src/generated/resources/assets/computercraft/models/item/redstone_relay.json create mode 100644 projects/common/src/generated/resources/data/computercraft/advancements/recipes/redstone/redstone_relay.json create mode 100644 projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/redstone_relay.json create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlock.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlockEntity.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayPeripheral.java create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_bottom.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_front.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_side.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_top.png create mode 100644 projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Relay_Test.kt create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal.snbt create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal_reverse.snbt create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/relay_test.self_output_update.snbt create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/relay_test.set_and_destroy.snbt create mode 100644 projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneMethods.java create mode 100644 projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneAccess.java create mode 100644 projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneState.java create mode 100644 projects/fabric/src/generated/resources/data/computercraft/recipes/redstone_relay.json create mode 100644 projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json create mode 100644 projects/forge/src/generated/resources/data/computercraft/recipes/redstone_relay.json create mode 100644 projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json create mode 100644 projects/web/src/htmlTransform/export/items/computercraft/redstone_relay.png create mode 100644 projects/web/src/htmlTransform/export/items/minecraft/stick.png diff --git a/doc/events/redstone.md b/doc/events/redstone.md index d097fc2b50..736363bf03 100644 --- a/doc/events/redstone.md +++ b/doc/events/redstone.md @@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers SPDX-License-Identifier: MPL-2.0 --> -The [`event!redstone`] event is fired whenever any redstone inputs on the computer change. +The [`event!redstone`] event is fired whenever any redstone inputs on the computer or [relay][`redstone_relay`] change. ## Return values 1. [`string`]: The event name. @@ -21,3 +21,7 @@ while true do print("A redstone input has changed!") end ``` + +## See also + - [The `redstone` API on computers][`module!redstone`] + - [The `redstone_relay` peripheral][`redstone_relay`] diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 87231a21f9..cb337ee27c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ junit = "5.10.1" jmh = "1.37" # Build tools -cctJavadoc = "1.8.2" +cctJavadoc = "1.8.3" checkstyle = "10.14.1" curseForgeGradle = "1.0.14" errorProne-core = "2.27.0" diff --git a/projects/common/src/generated/resources/assets/computercraft/blockstates/redstone_relay.json b/projects/common/src/generated/resources/assets/computercraft/blockstates/redstone_relay.json new file mode 100644 index 0000000000..5a813aa867 --- /dev/null +++ b/projects/common/src/generated/resources/assets/computercraft/blockstates/redstone_relay.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=east": {"model": "computercraft:block/redstone_relay", "y": 90}, + "facing=north": {"model": "computercraft:block/redstone_relay", "y": 0}, + "facing=south": {"model": "computercraft:block/redstone_relay", "y": 180}, + "facing=west": {"model": "computercraft:block/redstone_relay", "y": 270} + } +} diff --git a/projects/common/src/generated/resources/assets/computercraft/lang/en_us.json b/projects/common/src/generated/resources/assets/computercraft/lang/en_us.json index dde797f0e4..0802004c44 100644 --- a/projects/common/src/generated/resources/assets/computercraft/lang/en_us.json +++ b/projects/common/src/generated/resources/assets/computercraft/lang/en_us.json @@ -17,6 +17,7 @@ "block.computercraft.monitor_advanced": "Advanced Monitor", "block.computercraft.monitor_normal": "Monitor", "block.computercraft.printer": "Printer", + "block.computercraft.redstone_relay": "Redstone Relay", "block.computercraft.speaker": "Speaker", "block.computercraft.turtle_advanced": "Advanced Turtle", "block.computercraft.turtle_advanced.upgraded": "Advanced %s Turtle", diff --git a/projects/common/src/generated/resources/assets/computercraft/models/block/redstone_relay.json b/projects/common/src/generated/resources/assets/computercraft/models/block/redstone_relay.json new file mode 100644 index 0000000000..e35b88836a --- /dev/null +++ b/projects/common/src/generated/resources/assets/computercraft/models/block/redstone_relay.json @@ -0,0 +1,9 @@ +{ + "parent": "minecraft:block/orientable_with_bottom", + "textures": { + "bottom": "computercraft:block/redstone_relay_bottom", + "front": "computercraft:block/redstone_relay_front", + "side": "computercraft:block/redstone_relay_side", + "top": "computercraft:block/redstone_relay_top" + } +} diff --git a/projects/common/src/generated/resources/assets/computercraft/models/item/redstone_relay.json b/projects/common/src/generated/resources/assets/computercraft/models/item/redstone_relay.json new file mode 100644 index 0000000000..3ffb62221e --- /dev/null +++ b/projects/common/src/generated/resources/assets/computercraft/models/item/redstone_relay.json @@ -0,0 +1 @@ +{"parent": "computercraft:block/redstone_relay"} diff --git a/projects/common/src/generated/resources/data/computercraft/advancements/recipes/redstone/redstone_relay.json b/projects/common/src/generated/resources/data/computercraft/advancements/recipes/redstone/redstone_relay.json new file mode 100644 index 0000000000..d3a1b65e15 --- /dev/null +++ b/projects/common/src/generated/resources/data/computercraft/advancements/recipes/redstone/redstone_relay.json @@ -0,0 +1,16 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_cable": { + "conditions": {"items": [{"items": ["computercraft:wired_modem"]}]}, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": {"recipe": "computercraft:redstone_relay"}, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [["has_cable", "has_the_recipe"]], + "rewards": {"recipes": ["computercraft:redstone_relay"]}, + "sends_telemetry_event": false +} diff --git a/projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/redstone_relay.json b/projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/redstone_relay.json new file mode 100644 index 0000000000..1f2b43ff5a --- /dev/null +++ b/projects/common/src/generated/resources/data/computercraft/loot_tables/blocks/redstone_relay.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [{"condition": "minecraft:survives_explosion"}], + "entries": [{"type": "minecraft:item", "name": "computercraft:redstone_relay"}], + "rolls": 1.0 + } + ], + "random_sequence": "computercraft:blocks/redstone_relay" +} diff --git a/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java b/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java index 274557ad23..a33a277666 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java @@ -97,6 +97,8 @@ public static void addBlockModels(BlockModelGenerators generators) { registerCable(generators); + registerRedstoneControl(generators); + registerTurtleUpgrade(generators, "block/turtle_crafting_table", "block/turtle_crafty_face"); registerTurtleUpgrade(generators, "block/turtle_speaker", "block/turtle_speaker_face"); registerTurtleModem(generators, "block/turtle_modem_normal", "block/wireless_modem_normal_face"); @@ -355,6 +357,18 @@ private static void registerCable(BlockModelGenerators generators) { generators.blockStateOutput.accept(generator); } + private static void registerRedstoneControl(BlockModelGenerators generators) { + var redstoneControl = ModRegistry.Blocks.REDSTONE_RELAY.get(); + var model = ModelTemplates.CUBE_ORIENTABLE_TOP_BOTTOM.create( + redstoneControl, TextureMapping.orientableCube(redstoneControl), generators.modelOutput + ); + generators.blockStateOutput.accept( + MultiVariantGenerator.multiVariant(redstoneControl, Variant.variant().with(VariantProperties.MODEL, model)) + .with(createHorizontalFacingDispatch()) + ); + } + + private static final BooleanProperty[] CABLE_DIRECTIONS = { CableBlock.DOWN, CableBlock.UP, CableBlock.NORTH, CableBlock.SOUTH, CableBlock.WEST, CableBlock.EAST }; private static final boolean[] BOOLEANS = new boolean[]{ false, true }; diff --git a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java index 1f23f7873f..09713d21a3 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java @@ -80,6 +80,7 @@ private void addTranslations() { add(ModRegistry.Items.WIRED_MODEM.get(), "Wired Modem"); add(ModRegistry.Items.CABLE.get(), "Networking Cable"); add(ModRegistry.Items.WIRED_MODEM_FULL.get(), "Wired Modem"); + add(ModRegistry.Items.REDSTONE_RELAY.get(), "Redstone Relay"); add(ModRegistry.Items.TURTLE_NORMAL.get(), "Turtle"); add(ModRegistry.Blocks.TURTLE_NORMAL.get().getDescriptionId() + ".upgraded", "%s Turtle"); diff --git a/projects/common/src/main/java/dan200/computercraft/data/LootTableProvider.java b/projects/common/src/main/java/dan200/computercraft/data/LootTableProvider.java index 5f0f5b5ac7..bfd7f63198 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/LootTableProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/LootTableProvider.java @@ -51,6 +51,7 @@ private static void registerBlocks(BiConsumer add) { .requires(ingredients.string()) .unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get())) .save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add)); + + ShapedRecipeBuilder + .shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.REDSTONE_RELAY.get()) + .pattern("SRS") + .pattern("RCR") + .pattern("SRS") + .define('S', Items.STONE) + .define('R', ingredients.redstone()) + .define('C', ModRegistry.Blocks.CABLE.get()) + .unlockedBy("has_cable", inventoryChange(ModRegistry.Blocks.CABLE.get())) + .save(add); } private static DyeColor ofColour(Colour colour) { diff --git a/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java b/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java index 795ecca151..04cf493da4 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/TagProvider.java @@ -78,9 +78,12 @@ public static void blockTags(TagConsumer tags) { ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get(), ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get(), ModRegistry.Blocks.WIRED_MODEM_FULL.get(), - ModRegistry.Blocks.CABLE.get() + ModRegistry.Blocks.CABLE.get(), + ModRegistry.Blocks.REDSTONE_RELAY.get() ); + tags.tag(BlockTags.MINEABLE_WITH_AXE).add(ModRegistry.Blocks.LECTERN.get()); + tags.tag(BlockTags.WITHER_IMMUNE).add(ModRegistry.Blocks.COMPUTER_COMMAND.get()); tags.tag(ExternalModTags.Blocks.CREATE_BRITTLE).add( diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java index 5cb731e146..b12a8f0cac 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -62,6 +62,8 @@ import dan200.computercraft.shared.peripheral.printer.PrinterBlock; import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity; import dan200.computercraft.shared.peripheral.printer.PrinterMenu; +import dan200.computercraft.shared.peripheral.redstone.RedstoneRelayBlock; +import dan200.computercraft.shared.peripheral.redstone.RedstoneRelayBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerBlock; import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; import dan200.computercraft.shared.platform.PlatformHelper; @@ -132,7 +134,7 @@ private static BlockBehaviour.Properties properties() { return BlockBehaviour.Properties.of().strength(2); } - private static BlockBehaviour.Properties computerProperties() { + private static BlockBehaviour.Properties redstoneConductor() { // Computers shouldn't conduct redstone through them, so set isRedstoneConductor to false. This still allows // redstone to connect to computers though as it's a signal source. return properties().isRedstoneConductor((block, level, blockPos) -> false); @@ -147,11 +149,11 @@ private static BlockBehaviour.Properties modemProperties() { } public static final RegistryEntry> COMPUTER_NORMAL = REGISTRY.register("computer_normal", - () -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL)); + () -> new ComputerBlock<>(redstoneConductor().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL)); public static final RegistryEntry> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced", - () -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED)); + () -> new ComputerBlock<>(redstoneConductor().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED)); public static final RegistryEntry> COMPUTER_COMMAND = REGISTRY.register("computer_command", - () -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND)); + () -> new CommandComputerBlock<>(redstoneConductor().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND)); public static final RegistryEntry TURTLE_NORMAL = REGISTRY.register("turtle_normal", () -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), BlockEntities.TURTLE_NORMAL)); @@ -179,6 +181,9 @@ private static BlockBehaviour.Properties modemProperties() { public static final RegistryEntry LECTERN = REGISTRY.register("lectern", () -> new CustomLecternBlock( BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.5F).sound(SoundType.WOOD).ignitedByLava() )); + + public static final RegistryEntry REDSTONE_RELAY = REGISTRY.register("redstone_relay", + () -> new RedstoneRelayBlock(redstoneConductor().mapColor(MapColor.STONE))); } public static class BlockEntities { @@ -222,6 +227,8 @@ private static RegistryEntry> ofBlock ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true)); public static final RegistryEntry> LECTERN = ofBlock(Blocks.LECTERN, CustomLecternBlockEntity::new); + + public static final RegistryEntry> REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, RedstoneRelayBlockEntity::new); } public static final class Items { @@ -267,6 +274,7 @@ private static RegistryEntry ofBlock(Regist public static final RegistryEntry WIRELESS_MODEM_NORMAL = ofBlock(Blocks.WIRELESS_MODEM_NORMAL, BlockItem::new); public static final RegistryEntry WIRELESS_MODEM_ADVANCED = ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, BlockItem::new); public static final RegistryEntry WIRED_MODEM_FULL = ofBlock(Blocks.WIRED_MODEM_FULL, BlockItem::new); + public static final RegistryEntry REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, BlockItem::new); public static final RegistryEntry CABLE = REGISTRY.register("cable", () -> new CableBlockItem.Cable(Blocks.CABLE.get(), properties())); @@ -415,6 +423,7 @@ static class CreativeTabs { out.accept(Items.CABLE.get()); out.accept(Items.WIRED_MODEM.get()); out.accept(Items.WIRED_MODEM_FULL.get()); + out.accept(Items.REDSTONE_RELAY.get()); out.accept(Items.MONITOR_NORMAL.get()); out.accept(Items.MONITOR_ADVANCED.get()); 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 e0f5e688bd..aaceb04eef 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 @@ -127,7 +127,7 @@ protected void serverTick() { // Update the block state if needed. updateBlockState(computer.getState()); - var changes = computer.pollAndResetChanges(); + var changes = computer.pollRedstoneChanges(); if (changes != 0) { for (var direction : DirectionUtil.FACINGS) { if ((changes & (1 << remapToLocalSide(direction).ordinal())) != 0) updateRedstoneTo(direction); @@ -195,8 +195,10 @@ private void updateRedstoneInput(ServerComputer computer, Direction dir, BlockPo var offsetSide = dir.getOpposite(); var localDir = remapToLocalSide(dir); - computer.setRedstoneInput(localDir, RedstoneUtil.getRedstoneInput(getLevel(), targetPos, dir)); - computer.setBundledRedstoneInput(localDir, BundledRedstone.getOutput(getLevel(), targetPos, offsetSide)); + computer.setRedstoneInput(localDir, + RedstoneUtil.getRedstoneInput(getLevel(), targetPos, dir), + BundledRedstone.getOutput(getLevel(), targetPos, offsetSide) + ); } /** diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index 808f38cbb7..3a7d08cec1 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -134,8 +134,8 @@ boolean hasTimedOut() { * * @return What sides on the computer have changed. */ - public int pollAndResetChanges() { - return computer.pollAndResetChanges(); + public int pollRedstoneChanges() { + return computer.pollRedstoneChanges(); } public UUID register() { @@ -222,19 +222,15 @@ public void queueEvent(String event, @Nullable Object[] arguments) { } public int getRedstoneOutput(ComputerSide side) { - return computer.getEnvironment().getExternalRedstoneOutput(side); + return computer.isOn() ? computer.getRedstone().getExternalOutput(side) : 0; } - public void setRedstoneInput(ComputerSide side, int level) { - computer.getEnvironment().setRedstoneInput(side, level); + public void setRedstoneInput(ComputerSide side, int level, int bundledState) { + computer.getRedstone().setInput(side, level, bundledState); } public int getBundledRedstoneOutput(ComputerSide side) { - return computer.getEnvironment().getExternalBundledRedstoneOutput(side); - } - - public void setBundledRedstoneInput(ComputerSide side, int combination) { - computer.getEnvironment().setBundledRedstoneInput(side, combination); + return computer.isOn() ? computer.getRedstone().getExternalBundledOutput(side) : 0; } public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlock.java new file mode 100644 index 0000000000..61146ee096 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlock.java @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 +package dan200.computercraft.shared.peripheral.redstone; + +import dan200.computercraft.shared.common.IBundledRedstoneBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; + +import javax.annotation.Nonnull; + +/** + * The block for redstone relays. This mostly just forwards method calls to the {@linkplain RedstoneRelayBlockEntity + * block entity}. + */ +public final class RedstoneRelayBlock extends HorizontalDirectionalBlock implements EntityBlock, IBundledRedstoneBlock { + public RedstoneRelayBlock(Properties properties) { + super(properties); + registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder properties) { + properties.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext placement) { + return defaultBlockState().setValue(FACING, placement.getHorizontalDirection()); + } + + @Override + @Deprecated + public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + super.tick(state, level, pos, random); + + if (level.getBlockEntity(pos) instanceof RedstoneRelayBlockEntity relay) relay.update(); + } + + @Override + @Deprecated + public boolean isSignalSource(@Nonnull BlockState state) { + return true; + } + + @Override + @Deprecated + public int getDirectSignal(@Nonnull BlockState state, BlockGetter level, @Nonnull BlockPos pos, @Nonnull Direction incomingSide) { + return level.getBlockEntity(pos) instanceof RedstoneRelayBlockEntity relay ? relay.getRedstoneOutput(incomingSide.getOpposite()) : 0; + } + + @Override + @Deprecated + public int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + return getDirectSignal(state, level, pos, direction); + } + + @Override + public int getBundledRedstoneOutput(Level level, BlockPos pos, Direction side) { + return level.getBlockEntity(pos) instanceof RedstoneRelayBlockEntity relay ? relay.getBundledRedstoneOutput(side) : 0; + } + + @Override + @Deprecated + public void neighborChanged(@Nonnull BlockState state, @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Block neighbourBlock, @Nonnull BlockPos neighbourPos, boolean isMoving) { + if (world.getBlockEntity(pos) instanceof RedstoneRelayBlockEntity relay) relay.neighborChanged(neighbourPos); + } + + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new RedstoneRelayBlockEntity(pos, state); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlockEntity.java new file mode 100644 index 0000000000..833b0d4ad2 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayBlockEntity.java @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 +package dan200.computercraft.shared.peripheral.redstone; + +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.computer.ComputerSide; +import dan200.computercraft.core.redstone.RedstoneState; +import dan200.computercraft.impl.BundledRedstone; +import dan200.computercraft.shared.ModRegistry; +import dan200.computercraft.shared.util.DirectionUtil; +import dan200.computercraft.shared.util.RedstoneUtil; +import dan200.computercraft.shared.util.TickScheduler; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public final class RedstoneRelayBlockEntity extends BlockEntity { + private final TickScheduler.Token tickToken = new TickScheduler.Token(this); + + private final RedstoneState redstoneState = new RedstoneState(() -> TickScheduler.schedule(tickToken)); + private final RedstoneRelayPeripheral peripheral = new RedstoneRelayPeripheral(redstoneState); + + public RedstoneRelayBlockEntity(BlockPos pos, BlockState blockState) { + super(ModRegistry.BlockEntities.REDSTONE_RELAY.get(), pos, blockState); + } + + @Override + public void clearRemoved() { + super.clearRemoved(); + TickScheduler.schedule(tickToken); + } + + void update() { + var changes = redstoneState.updateOutput(); + if (changes != 0) { + for (var direction : DirectionUtil.FACINGS) { + if ((changes & (1 << mapSide(direction).ordinal())) != 0) updateRedstoneTo(direction); + } + } + + if (redstoneState.pollInputChanged()) peripheral.queueRedstoneEvent(); + } + + void neighborChanged(BlockPos neighbour) { + for (var dir : DirectionUtil.FACINGS) { + var offset = getBlockPos().relative(dir); + if (offset.equals(neighbour)) { + updateRedstoneInput(dir, offset, false); + return; + } + } + + // If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods + // handle this incorrectly. + for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(dir, getBlockPos().relative(dir), false); + } + + private void updateRedstoneTo(Direction direction) { + RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), direction); + updateRedstoneInput(direction, getBlockPos().relative(direction), true); + } + + private void updateRedstoneInput(Direction dir, BlockPos targetPos, boolean ticking) { + var changed = redstoneState.setInput(mapSide(dir), + RedstoneUtil.getRedstoneInput(getLevel(), targetPos, dir), + BundledRedstone.getOutput(getLevel(), targetPos, dir.getOpposite()) + ); + + // If the input has changed, and we're not currently in update(), then schedule a new tick so we can queue a + // redstone event. + if (changed && !ticking) TickScheduler.schedule(tickToken); + } + + private ComputerSide mapSide(Direction globalSide) { + return DirectionUtil.toLocal(getBlockState().getValue(HorizontalDirectionalBlock.FACING), globalSide); + } + + int getRedstoneOutput(Direction side) { + return redstoneState.getExternalOutput(mapSide(side)); + } + + int getBundledRedstoneOutput(Direction side) { + return redstoneState.getExternalBundledOutput(mapSide(side)); + } + + public IPeripheral peripheral() { + return peripheral; + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayPeripheral.java new file mode 100644 index 0000000000..e0a73a1074 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/redstone/RedstoneRelayPeripheral.java @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 +package dan200.computercraft.shared.peripheral.redstone; + +import dan200.computercraft.api.peripheral.AttachedComputerSet; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.apis.RedstoneAPI; +import dan200.computercraft.core.apis.RedstoneMethods; +import dan200.computercraft.core.redstone.RedstoneAccess; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * The redstone relay is a peripheral that allows reading and outputting redstone signals. + *

+ * The peripheral provides largely identical methods to a computer's built-in {@link RedstoneAPI} API, allowing setting + * signals on all six sides of the block ("top", "bottom", "left", "right", "front" and "back"). + * + *

+ * ## Recipe + *

+ * + *
+ * + * @cc.usage Toggle the redstone signal above the computer every 0.5 seconds. + * + *
{@code
+ * local relay = peripheral.find("redstone_relay")
+ * while true do
+ *   relay.setOutput("top", not relay.getOutput("top"))
+ *   sleep(0.5)
+ * end
+ * }
+ * @cc.module redstone_relay + * @cc.since 1.114.0 + */ +public final class RedstoneRelayPeripheral extends RedstoneMethods implements IPeripheral { + private final AttachedComputerSet computers = new AttachedComputerSet(); + + RedstoneRelayPeripheral(RedstoneAccess access) { + super(access); + } + + @Nonnull + @Override + public String getType() { + return "redstone_relay"; + } + + @Override + public boolean equals(@Nullable IPeripheral other) { + return this == other; + } + + @Override + public void attach(IComputerAccess computer) { + computers.add(computer); + } + + @Override + public void detach(IComputerAccess computer) { + computers.remove(computer); + } + + void queueRedstoneEvent() { + computers.queueEvent("redstone"); + } +} diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_bottom.png b/projects/common/src/main/resources/assets/computercraft/textures/block/redstone_relay_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..cad4772d690c79eba1b589ba9dede8b610e0a616 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Uw}`DtFp4Pii(Q5y1J&Orna`W zuCA`WzP^Ehfsv7sv9YnKsi~QnnT3Ugm6esXwY80ghPS>>addQ5RaI?mZGC-xLqkJz zbMr=KhAWIr{~4I;LdE)lCii)|IEF}Ej_p6_c-TPXXr0I0mV{T=gx072ceNDJ%V6}p zdj50m{ix`5e{=HX-AgB{D4x8s_!_4$N7kLyu5;ZOBUY>L@tCAj>c~2Wt9Yey-O1dZ z*=j3qiAdZ(&-frc_ax8bpAw#vvez=)IlhBCWqq1L;v|>rue7oj?b}i>%Aix);=`&o kYt_HoYdQ4v9W=Hfxf=J zuCA_%ii)+hwYs{xnVFf9kx_kpeRFejZEbB;Rh6curna_rLqkJxbhM3zhO)Bqe+K3& zj7(NmR<<@asi~aCeUuWaSja#>FwYRrlvu2H#mzS@vujqT7V?eoC zo-U3d5|XS34TG2id0Y;fPr8tDf2+)Sn-4?F4n+oo1CsKecnmfzRd9}piVih-pgcDsq{V&G`lGuWg0Gq%u>U1; z=;P^y2bU_m-l%o&-Qwe3^`bvACI{OIR9|$s{D$|#s_gY$Y}cOpY?(TTTVZC==T-ln eaXRp9$p8P|)ouB~zyCoY$KdJe=d#Wzp$Pz9_p zH8nFcGcYhPFE1}GEiEJ@Bs)7hCnqO1Ha0RcGJk)6gM)*8eSLa*dMPO>D=RC2fPiyb zTRbQzBO@dK00Yzk0y;W6u>$~0OG~e>uS+j1v9YnWwY5J#Kdh{*N=iyiO-)4qa$o=e z0Fg;VK~xwS1;Ig113?f3!OWgn){>Fn%K!hy(elw|)m=o0sG@K8Qn?F&fokGcf`K?- zULI#)wV?n?J`n%{@K@hv1{f19R>?|G7yzOsnys*IXjp$06H#HT+3_~Pcka#J>o z^SEk}{BB51s96CyHv>addQ5RaI?mZGC-xLqkJz zbMr=KhAWIr{~4I;LdE)lCii)|IEF}Ej_p6_c-TPXXr0I0mV{T=gx072ceNDJ%V6}p zdj50m{ix`5e{=HX-AgB{D4x8s_!_4$N7kLyu5;ZOBUY>L@tCAj>c~2Wt9Yey-O1XX z-D)duiAdZ(&-frc_ax8bpAw#vvez=)Iktl{Wqq1L;v|>rue7oj?b}i>%Aix);=`&o jYt_HoYdQ x.setValue(LeverBlock.POWERED, true) } + } + thenIdle(3) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") } + } + + /** + * Similar to the above, but with a repeater before the relay + * + * @see [Computer_Test.No_through_signal_reverse] + */ + @GameTest + fun No_through_signal_reverse(context: GameTestHelper) = context.sequence { + val lamp = BlockPos(2, 2, 4) + val lever = BlockPos(2, 2, 0) + thenExecute { + context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") + context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) } + } + thenIdle(3) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") } + } + + /** + * Check relays propagate redstone to surrounding blocks. + * + * @see [Computer_Test.Set_and_destroy] + */ + @GameTest + fun Set_and_destroy(context: GameTestHelper) = context.sequence { + val lamp = BlockPos(2, 2, 3) + + thenExecute { + val peripheral = context.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.REDSTONE_RELAY.get()) + .peripheral() + as RedstoneRelayPeripheral + peripheral.setOutput(ComputerSide.BACK, true) + } + thenIdle(1) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, true, "Lamp should be lit") } + thenExecute { context.setBlock(BlockPos(2, 2, 2), Blocks.AIR) } + thenIdle(4) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") } + } + + /** + * Check relays pick up propagated redstone to surrounding blocks. + * + * @see [Computer_Test.Self_output_update] + */ + @GameTest + fun Self_output_update(context: GameTestHelper) = context.sequence { + fun relay() = context.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.REDSTONE_RELAY.get()) + .peripheral() as RedstoneRelayPeripheral + + thenExecute { relay().setOutput(ComputerSide.BACK, true) } + thenIdle(2) + thenExecute { assertEquals(true, relay().getInput(ComputerSide.BACK), "Input should be on") } + + thenIdle(2) + + thenExecute { relay().setOutput(ComputerSide.BACK, false) } + thenIdle(2) + thenExecute { assertEquals(false, relay().getInput(ComputerSide.BACK), "Input should be off") } + } +} diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt index 149f947ce3..a97363f8e8 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt @@ -92,6 +92,7 @@ object TestHooks { Printer_Test::class.java, Printout_Test::class.java, Recipe_Test::class.java, + Relay_Test::class.java, Speaker_Test::class.java, Turtle_Test::class.java, ) diff --git a/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal.snbt b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal.snbt new file mode 100644 index 0000000000..008775e567 --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal.snbt @@ -0,0 +1,141 @@ +{ + DataVersion: 2730, + 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:lever{face:floor,facing:south,powered:false}"}, + {pos: [2, 1, 1], state: "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}"}, + {pos: [2, 1, 2], state: "computercraft:redstone_relay{facing:north}"}, + {pos: [2, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:none}"}, + {pos: [2, 1, 4], state: "minecraft:redstone_lamp{lit:false}"}, + {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:redstone_lamp{lit:false}", + "computercraft:redstone_relay{facing:north}", + "minecraft:air", + "minecraft:lever{face:floor,facing:south,powered:false}", + "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}", + "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:none}" + ] +} diff --git a/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal_reverse.snbt b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal_reverse.snbt new file mode 100644 index 0000000000..d49021b89f --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.no_through_signal_reverse.snbt @@ -0,0 +1,141 @@ +{ + 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:lever{face:floor,facing:south,powered:false}"}, + {pos: [2, 1, 1], state: "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}"}, + {pos: [2, 1, 2], state: "computercraft:redstone_relay{facing:north}"}, + {pos: [2, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, + {pos: [2, 1, 4], state: "minecraft:redstone_lamp{lit:false}"}, + {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:redstone_lamp{lit:false}", + "minecraft:air", + "minecraft:lever{face:floor,facing:south,powered:false}", + "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}", + "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", + "computercraft:redstone_relay{facing:north}" + ] +} diff --git a/projects/common/src/testMod/resources/data/cctest/structures/relay_test.self_output_update.snbt b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.self_output_update.snbt new file mode 100644 index 0000000000..c0aacd1f4b --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/relay_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:redstone_relay{facing:north}"}, + {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:redstone_relay{facing:north}" + ] +} diff --git a/projects/common/src/testMod/resources/data/cctest/structures/relay_test.set_and_destroy.snbt b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.set_and_destroy.snbt new file mode 100644 index 0000000000..2078950b02 --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/relay_test.set_and_destroy.snbt @@ -0,0 +1,138 @@ +{ + 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:redstone_relay{facing:north}"}, + {pos: [2, 1, 3], state: "minecraft:redstone_lamp{lit:false}"}, + {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:redstone_lamp{lit:false}", + "minecraft:air", + "computercraft:redstone_relay{facing:north}" + ] +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java b/projects/core/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java index a8b3e9112d..046fc3ac95 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java @@ -43,18 +43,6 @@ interface IPeripheralChangeListener { void queueEvent(String event, @Nullable Object... args); - void setOutput(ComputerSide side, int output); - - int getOutput(ComputerSide side); - - int getInput(ComputerSide side); - - void setBundledOutput(ComputerSide side, int output); - - int getBundledOutput(ComputerSide side); - - int getBundledInput(ComputerSide side); - void setPeripheralChangeListener(@Nullable IPeripheralChangeListener listener); @Nullable diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java b/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java index 66f52ad314..212afca300 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneAPI.java @@ -5,9 +5,9 @@ package dan200.computercraft.core.apis; import dan200.computercraft.api.lua.ILuaAPI; -import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.core.computer.ComputerSide; +import dan200.computercraft.core.redstone.RedstoneAccess; import java.util.List; @@ -53,11 +53,9 @@ * the Minecraft wiki." * @cc.module redstone */ -public class RedstoneAPI implements ILuaAPI { - private final IAPIEnvironment environment; - - public RedstoneAPI(IAPIEnvironment environment) { - this.environment = environment; +public class RedstoneAPI extends RedstoneMethods implements ILuaAPI { + public RedstoneAPI(RedstoneAccess environment) { + super(environment); } @Override @@ -76,131 +74,4 @@ public String[] getNames() { public final List getSides() { return ComputerSide.NAMES; } - - /** - * Turn the redstone signal of a specific side on or off. - * - * @param side The side to set. - * @param on Whether the redstone signal should be on or off. When on, a signal strength of 15 is emitted. - */ - @LuaFunction - public final void setOutput(ComputerSide side, boolean on) { - environment.setOutput(side, on ? 15 : 0); - } - - /** - * Get the current redstone output of a specific side. - * - * @param side The side to get. - * @return Whether the redstone output is on or off. - * @see #setOutput - */ - @LuaFunction - public final boolean getOutput(ComputerSide side) { - return environment.getOutput(side) > 0; - } - - /** - * Get the current redstone input of a specific side. - * - * @param side The side to get. - * @return Whether the redstone input is on or off. - */ - @LuaFunction - public final boolean getInput(ComputerSide side) { - return environment.getInput(side) > 0; - } - - /** - * Set the redstone signal strength for a specific side. - * - * @param side The side to set. - * @param value The signal strength between 0 and 15. - * @throws LuaException If {@code value} is not between 0 and 15. - * @cc.since 1.51 - */ - @LuaFunction({ "setAnalogOutput", "setAnalogueOutput" }) - public final void setAnalogOutput(ComputerSide side, int value) throws LuaException { - if (value < 0 || value > 15) throw new LuaException("Expected number in range 0-15"); - environment.setOutput(side, value); - } - - /** - * Get the redstone output signal strength for a specific side. - * - * @param side The side to get. - * @return The output signal strength, between 0 and 15. - * @cc.since 1.51 - * @see #setAnalogOutput - */ - @LuaFunction({ "getAnalogOutput", "getAnalogueOutput" }) - public final int getAnalogOutput(ComputerSide side) { - return environment.getOutput(side); - } - - /** - * Get the redstone input signal strength for a specific side. - * - * @param side The side to get. - * @return The input signal strength, between 0 and 15. - * @cc.since 1.51 - */ - @LuaFunction({ "getAnalogInput", "getAnalogueInput" }) - public final int getAnalogInput(ComputerSide side) { - return environment.getInput(side); - } - - /** - * Set the bundled cable output for a specific side. - * - * @param side The side to set. - * @param output The colour bitmask to set. - * @cc.see colors.subtract For removing a colour from the bitmask. - * @cc.see colors.combine For adding a color to the bitmask. - */ - @LuaFunction - public final void setBundledOutput(ComputerSide side, int output) { - environment.setBundledOutput(side, output); - } - - /** - * Get the bundled cable output for a specific side. - * - * @param side The side to get. - * @return The bundle cable's output. - */ - @LuaFunction - public final int getBundledOutput(ComputerSide side) { - return environment.getBundledOutput(side); - } - - /** - * Get the bundled cable input for a specific side. - * - * @param side The side to get. - * @return The bundle cable's input. - * @see #testBundledInput To determine if a specific colour is set. - */ - @LuaFunction - public final int getBundledInput(ComputerSide side) { - return environment.getBundledInput(side); - } - - /** - * Determine if a specific combination of colours are on for the given side. - * - * @param side The side to test. - * @param mask The mask to test. - * @return If the colours are on. - * @cc.usage Check if [`colors.white`] and [`colors.black`] are on above the computer. - *
{@code
-     * print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
-     * }
- * @see #getBundledInput - */ - @LuaFunction - public final boolean testBundledInput(ComputerSide side, int mask) { - var input = environment.getBundledInput(side); - return (input & mask) == mask; - } } diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneMethods.java b/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneMethods.java new file mode 100644 index 0000000000..cd029a3f9c --- /dev/null +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/RedstoneMethods.java @@ -0,0 +1,147 @@ +// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. +// +// SPDX-License-Identifier: LicenseRef-CCPL +package dan200.computercraft.core.apis; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.core.computer.ComputerSide; +import dan200.computercraft.core.redstone.RedstoneAccess; + +/** + * A base class for all blocks with redstone integration. + */ +public class RedstoneMethods { + private final RedstoneAccess redstone; + + public RedstoneMethods(RedstoneAccess redstone) { + this.redstone = redstone; + } + + /** + * Turn the redstone signal of a specific side on or off. + * + * @param side The side to set. + * @param on Whether the redstone signal should be on or off. When on, a signal strength of 15 is emitted. + */ + @LuaFunction + public final void setOutput(ComputerSide side, boolean on) { + redstone.setOutput(side, on ? 15 : 0); + } + + /** + * Get the current redstone output of a specific side. + * + * @param side The side to get. + * @return Whether the redstone output is on or off. + * @see #setOutput + */ + @LuaFunction + public final boolean getOutput(ComputerSide side) { + return redstone.getOutput(side) > 0; + } + + /** + * Get the current redstone input of a specific side. + * + * @param side The side to get. + * @return Whether the redstone input is on or off. + */ + @LuaFunction + public final boolean getInput(ComputerSide side) { + return redstone.getInput(side) > 0; + } + + /** + * Set the redstone signal strength for a specific side. + * + * @param side The side to set. + * @param value The signal strength between 0 and 15. + * @throws LuaException If {@code value} is not between 0 and 15. + * @cc.since 1.51 + */ + @LuaFunction({ "setAnalogOutput", "setAnalogueOutput" }) + public final void setAnalogOutput(ComputerSide side, int value) throws LuaException { + if (value < 0 || value > 15) throw new LuaException("Expected number in range 0-15"); + redstone.setOutput(side, value); + } + + /** + * Get the redstone output signal strength for a specific side. + * + * @param side The side to get. + * @return The output signal strength, between 0 and 15. + * @cc.since 1.51 + * @see #setAnalogOutput + */ + @LuaFunction({ "getAnalogOutput", "getAnalogueOutput" }) + public final int getAnalogOutput(ComputerSide side) { + return redstone.getOutput(side); + } + + /** + * Get the redstone input signal strength for a specific side. + * + * @param side The side to get. + * @return The input signal strength, between 0 and 15. + * @cc.since 1.51 + */ + @LuaFunction({ "getAnalogInput", "getAnalogueInput" }) + public final int getAnalogInput(ComputerSide side) { + return redstone.getInput(side); + } + + /** + * Set the bundled cable output for a specific side. + * + * @param side The side to set. + * @param output The colour bitmask to set. + * @cc.see colors.subtract For removing a colour from the bitmask. + * @cc.see colors.combine For adding a color to the bitmask. + */ + @LuaFunction + public final void setBundledOutput(ComputerSide side, int output) { + redstone.setBundledOutput(side, output); + } + + /** + * Get the bundled cable output for a specific side. + * + * @param side The side to get. + * @return The bundle cable's output. + */ + @LuaFunction + public final int getBundledOutput(ComputerSide side) { + return redstone.getBundledOutput(side); + } + + /** + * Get the bundled cable input for a specific side. + * + * @param side The side to get. + * @return The bundle cable's input. + * @see #testBundledInput To determine if a specific colour is set. + */ + @LuaFunction + public final int getBundledInput(ComputerSide side) { + return redstone.getBundledInput(side); + } + + /** + * Determine if a specific combination of colours are on for the given side. + * + * @param side The side to test. + * @param mask The mask to test. + * @return If the colours are on. + * @cc.usage Check if [`colors.white`] and [`colors.black`] are on above this block. + *
{@code
+     * print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
+     * }
+ * @see #getBundledInput + */ + @LuaFunction + public final boolean testBundledInput(ComputerSide side, int mask) { + var input = redstone.getBundledInput(side); + return (input & mask) == mask; + } +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java b/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java index 9b853bab6b..e3cb9f2e73 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java @@ -12,10 +12,10 @@ import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.computer.mainthread.MainThreadScheduler; import dan200.computercraft.core.filesystem.FileSystem; +import dan200.computercraft.core.redstone.RedstoneState; import dan200.computercraft.core.terminal.Terminal; import javax.annotation.Nullable; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** @@ -54,8 +54,7 @@ public class Computer { // Additional state about the computer and its environment. private final Environment internalEnvironment; - - private final AtomicInteger externalOutputChanges = new AtomicInteger(); + private final RedstoneState redstone = new RedstoneState(); private boolean startRequested; private int ticksSinceStart = -1; @@ -87,6 +86,10 @@ public Environment getEnvironment() { return internalEnvironment; } + public RedstoneState getRedstone() { + return redstone; + } + public IAPIEnvironment getAPIEnvironment() { return internalEnvironment; } @@ -157,10 +160,8 @@ public void tick() { executor.tick(); // Update the environment's internal state. + if (redstone.pollInputChanged()) queueEvent("redstone", null); internalEnvironment.tick(); - - // Propagate the environment's output to the world. - externalOutputChanges.accumulateAndGet(internalEnvironment.updateOutput(), (x, y) -> x | y); } /** @@ -168,8 +169,8 @@ public void tick() { * * @return What sides on the computer have changed. */ - public int pollAndResetChanges() { - return externalOutputChanges.getAndSet(0); + public int pollRedstoneChanges() { + return redstone.updateOutput(); } public boolean isBlinking() { diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java b/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java index 4abed1117c..d9db834722 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java @@ -153,11 +153,11 @@ final class ComputerExecutor implements ComputerScheduler.Worker { luaMethods = context.luaMethods(); executor = context.computerScheduler().createExecutor(this, metrics); - var environment = computer.getEnvironment(); + var environment = computer.getAPIEnvironment(); // Add all default APIs to the loaded list. addApi(new TermAPI(environment)); - addApi(new RedstoneAPI(environment)); + addApi(new RedstoneAPI(computer.getRedstone())); addApi(new FSAPI(environment)); addApi(new PeripheralAPI(environment, context.peripheralMethods())); addApi(new OSAPI(environment)); @@ -434,14 +434,13 @@ private void shutdown() throws InterruptedException { // Shutdown our APIs for (var api : apis) api.shutdown(); computer.getEnvironment().reset(); + computer.getRedstone().clearOutput(); // Unload filesystem if (fileSystem != null) { fileSystem.close(); fileSystem = null; } - - computer.getEnvironment().resetOutput(); } finally { isOnLock.unlock(); } diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/Environment.java b/projects/core/src/main/java/dan200/computercraft/core/computer/Environment.java index 15d38bfa1d..d1e10d5c45 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/Environment.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/Environment.java @@ -16,24 +16,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Iterator; /** * Represents the "environment" that a {@link Computer} exists in. *

- * This handles storing and updating of peripherals and redstone. - * - *

Redstone

- * We holds three kinds of arrays for redstone, in normal and bundled versions: - *
    - *
  • {@link #internalOutput} is the redstone output which the computer has currently set. This is read on both - * threads, and written on the computer thread.
  • - *
  • {@link #externalOutput} is the redstone output currently propagated to the world. This is only read and written - * on the main thread.
  • - *
  • {@link #input} is the redstone input from external sources. This is read on both threads, and written on the main - * thread.
  • - *
+ * This handles storing and updating of peripherals and timers. * *

Peripheral

* We also keep track of peripherals. These are read on both threads, and only written on the main thread. @@ -43,17 +31,6 @@ public final class Environment implements IAPIEnvironment { private final ComputerEnvironment environment; private final MetricsObserver metrics; - private boolean internalOutputChanged = false; - private final int[] internalOutput = new int[ComputerSide.COUNT]; - private final int[] internalBundledOutput = new int[ComputerSide.COUNT]; - - private final int[] externalOutput = new int[ComputerSide.COUNT]; - private final int[] externalBundledOutput = new int[ComputerSide.COUNT]; - - private boolean inputChanged = false; - private final int[] input = new int[ComputerSide.COUNT]; - private final int[] bundledInput = new int[ComputerSide.COUNT]; - private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT]; private @Nullable IPeripheralChangeListener peripheralListener = null; @@ -111,76 +88,6 @@ public void queueEvent(String event, @Nullable Object... args) { computer.queueEvent(event, args); } - @Override - public int getInput(ComputerSide side) { - return input[side.ordinal()]; - } - - @Override - public int getBundledInput(ComputerSide side) { - return bundledInput[side.ordinal()]; - } - - @Override - public void setOutput(ComputerSide side, int output) { - var index = side.ordinal(); - synchronized (internalOutput) { - if (internalOutput[index] != output) { - internalOutput[index] = output; - internalOutputChanged = true; - } - } - } - - @Override - public int getOutput(ComputerSide side) { - synchronized (internalOutput) { - return computer.isOn() ? internalOutput[side.ordinal()] : 0; - } - } - - @Override - public void setBundledOutput(ComputerSide side, int output) { - var index = side.ordinal(); - synchronized (internalOutput) { - if (internalBundledOutput[index] != output) { - internalBundledOutput[index] = output; - internalOutputChanged = true; - } - } - } - - @Override - public int getBundledOutput(ComputerSide side) { - synchronized (internalOutput) { - return computer.isOn() ? internalBundledOutput[side.ordinal()] : 0; - } - } - - public int getExternalRedstoneOutput(ComputerSide side) { - return computer.isOn() ? externalOutput[side.ordinal()] : 0; - } - - public int getExternalBundledRedstoneOutput(ComputerSide side) { - return computer.isOn() ? externalBundledOutput[side.ordinal()] : 0; - } - - public void setRedstoneInput(ComputerSide side, int level) { - var index = side.ordinal(); - if (input[index] != level) { - input[index] = level; - inputChanged = true; - } - } - - public void setBundledRedstoneInput(ComputerSide side, int combination) { - var index = side.ordinal(); - if (bundledInput[index] != combination) { - bundledInput[index] = combination; - inputChanged = true; - } - } - /** * Called when the computer starts up or shuts down, to reset any internal state. * @@ -197,11 +104,6 @@ void reset() { * Called on the main thread to update the internal state of the computer. */ void tick() { - if (inputChanged) { - inputChanged = false; - queueEvent("redstone"); - } - synchronized (timers) { // Countdown all of our active timers Iterator> it = timers.int2ObjectEntrySet().iterator(); @@ -218,45 +120,6 @@ void tick() { } } - /** - * Called on the main thread to propagate the internal outputs to the external ones. - * - * @return If the outputs have changed. - */ - int updateOutput() { - // Mark output as changed if the internal redstone has changed - synchronized (internalOutput) { - if (!internalOutputChanged) return 0; - - var changed = 0; - - for (var i = 0; i < ComputerSide.COUNT; i++) { - if (externalOutput[i] != internalOutput[i]) { - externalOutput[i] = internalOutput[i]; - changed |= 1 << i; - } - - if (externalBundledOutput[i] != internalBundledOutput[i]) { - externalBundledOutput[i] = internalBundledOutput[i]; - changed |= 1 << i; - } - } - - internalOutputChanged = false; - - return changed; - } - } - - void resetOutput() { - // Reset redstone output - synchronized (internalOutput) { - Arrays.fill(internalOutput, 0); - Arrays.fill(internalBundledOutput, 0); - internalOutputChanged = true; - } - } - @Nullable @Override public IPeripheral getPeripheral(ComputerSide side) { diff --git a/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneAccess.java b/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneAccess.java new file mode 100644 index 0000000000..524983e682 --- /dev/null +++ b/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneAccess.java @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 +package dan200.computercraft.core.redstone; + +import dan200.computercraft.core.apis.RedstoneMethods; +import dan200.computercraft.core.computer.ComputerSide; + +/** + * Common interface between blocks which provide and consume a redstone signal. + * + * @see RedstoneMethods Lua-facing methods wrapping this interface. + * @see RedstoneState A concrete implementation of this class. + */ +public interface RedstoneAccess { + /** + * Set the redstone output on a given side. + * + * @param side The side to set. + * @param output The output level, between 0 and 15. + */ + void setOutput(ComputerSide side, int output); + + /** + * Get the redstone output on a given side. + * + * @param side The side to get. + * @return The output level, between 0 and 15. + */ + int getOutput(ComputerSide side); + + /** + * Get the redstone input on a given side. + * + * @param side The side to get. + * @return The input level, between 0 and 15. + */ + int getInput(ComputerSide side); + + /** + * Set the bundled redstone output on a given side. + * + * @param side The side to set. + * @param output The output state, as a 16-bit bitmask. + */ + void setBundledOutput(ComputerSide side, int output); + + /** + * Get the bundled redstone output on a given side. + * + * @param side The side to get. + * @return The output state, as a 16-bit bitmask. + */ + int getBundledOutput(ComputerSide side); + + /** + * Set the bundled redstone input on a given side. + * + * @param side The side to get. + * @return The input state, as a 16-bit bitmask. + */ + int getBundledInput(ComputerSide side); +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneState.java b/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneState.java new file mode 100644 index 0000000000..e2a6f0d9f8 --- /dev/null +++ b/projects/core/src/main/java/dan200/computercraft/core/redstone/RedstoneState.java @@ -0,0 +1,248 @@ +// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. +// +// SPDX-License-Identifier: LicenseRef-CCPL +package dan200.computercraft.core.redstone; + +import dan200.computercraft.core.computer.ComputerSide; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import java.util.Arrays; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Manages the state of redstone inputs and ouputs on a computer (or other redstone emitting block). + *

+ * As computers execute on a separate thread to the main Minecraft world, computers cannot immediately read or write + * redstone values. Instead, we maintain a copy of the block's redstone inputs and outputs, and sync that with the + * Minecraft world when needed. + * + *

Input

+ * Redstone inputs should be propagated immediately to the internal state of the computer. Computers (and other redstone + * blocks) listen for block updates, fetch their neighbour's redstone state, and then call + * {@link #setInput(ComputerSide, int, int)}. + *

+ * However, we do not want to immediately schedule a {@code "redstone"} event, as otherwise we could schedule many + * events in a single tick. Instead, the next time the block is ticked, the consumer should call + * {@link #pollInputChanged()} and queue an event if needed. + * + *

Output

+ * In order to reduce block updates, we maintain a separate "internal" and "external" output state. Whenever a computer + * sets a redstone output, the "internal" state is updated, and a dirty flag is set. When the computer is ticked, + * {@link #updateOutput()} should be called, to copy the internal state to the external state. This returns a bitmask + * indicating which sides have changed. The external outputs may then be read with {@link #getExternalOutput(ComputerSide)} + * and {@link #getExternalBundledOutput(ComputerSide)}. + */ +public final class RedstoneState implements RedstoneAccess { + private final @Nullable Runnable onOutputChanged; + + private final ReentrantLock outputLock = new ReentrantLock(); + private @GuardedBy("outputLock") boolean internalOutputChanged = false; + private final @GuardedBy("outputLock") int[] internalOutput = new int[ComputerSide.COUNT]; + private final @GuardedBy("outputLock") int[] internalBundledOutput = new int[ComputerSide.COUNT]; + + private final int[] externalOutput = new int[ComputerSide.COUNT]; + private final int[] externalBundledOutput = new int[ComputerSide.COUNT]; + + private final ReentrantLock inputLock = new ReentrantLock(); + private boolean inputChanged = false; + private final @GuardedBy("inputLock") int[] input = new int[ComputerSide.COUNT]; + private final @GuardedBy("inputLock") int[] bundledInput = new int[ComputerSide.COUNT]; + + public RedstoneState() { + this(null); + } + + /** + * Construct a new {@link RedstoneState}, with a callback function to invoke when the internal output has + * changed. This function is called from the computer thread. + * + * @param outputChanged The function to invoke when output has changed. + */ + public RedstoneState(@Nullable Runnable outputChanged) { + this.onOutputChanged = outputChanged; + } + + @Override + public int getInput(ComputerSide side) { + inputLock.lock(); + try { + return input[side.ordinal()]; + } finally { + inputLock.unlock(); + } + } + + @Override + public int getBundledInput(ComputerSide side) { + inputLock.lock(); + try { + return bundledInput[side.ordinal()]; + } finally { + inputLock.unlock(); + } + } + + @Override + public void setOutput(ComputerSide side, int output) { + var index = side.ordinal(); + + outputLock.lock(); + try { + if (internalOutput[index] == output) return; + internalOutput[index] = output; + setOutputChanged(); + } finally { + outputLock.unlock(); + } + } + + @Override + public int getOutput(ComputerSide side) { + outputLock.lock(); + try { + return internalOutput[side.ordinal()]; + } finally { + outputLock.unlock(); + } + } + + @Override + public void setBundledOutput(ComputerSide side, int output) { + var index = side.ordinal(); + outputLock.lock(); + try { + if (internalBundledOutput[index] == output) return; + internalBundledOutput[index] = output; + setOutputChanged(); + } finally { + outputLock.unlock(); + } + } + + @Override + public int getBundledOutput(ComputerSide side) { + outputLock.lock(); + try { + return internalBundledOutput[side.ordinal()]; + } finally { + outputLock.unlock(); + } + } + + @GuardedBy("outputLock") + private void setOutputChanged() { + if (internalOutputChanged) return; + internalOutputChanged = true; + if (onOutputChanged != null) onOutputChanged.run(); + } + + /** + * Propagate redstone changes from the computer to the outside world. The effective outputs can be acquired with + * {@link #getExternalOutput(ComputerSide)} and {@link #getExternalBundledOutput(ComputerSide)}. + * + * @return A bitmask indicating which sides have changed (indexed via {@link ComputerSide#ordinal()}). + */ + public int updateOutput() { + outputLock.lock(); + try { + if (!internalOutputChanged) return 0; + + var changed = 0; + + for (var i = 0; i < ComputerSide.COUNT; i++) { + if (externalOutput[i] != internalOutput[i]) { + externalOutput[i] = internalOutput[i]; + changed |= 1 << i; + } + + if (externalBundledOutput[i] != internalBundledOutput[i]) { + externalBundledOutput[i] = internalBundledOutput[i]; + changed |= 1 << i; + } + } + + internalOutputChanged = false; + + return changed; + } finally { + outputLock.unlock(); + } + } + + /** + * Get the redstone output for a given side. + * + * @param side The side to get. + * @return The effective redstone output. + */ + public int getExternalOutput(ComputerSide side) { + return externalOutput[side.ordinal()]; + } + + /** + * Get the bundled redstone output for a given side. + * + * @param side The side to get. + * @return The effective bundled redstone output. + */ + public int getExternalBundledOutput(ComputerSide side) { + return externalBundledOutput[side.ordinal()]; + } + + /** + * Reset any redstone output set by the computer. + */ + public void clearOutput() { + outputLock.lock(); + try { + Arrays.fill(internalOutput, 0); + Arrays.fill(internalBundledOutput, 0); + internalOutputChanged = true; + } finally { + outputLock.unlock(); + } + } + + /** + * Set the redstone input for a given side. + * + * @param side The side to update. + * @param level The redstone level. + * @param bundledState The bundled redstone state. + * @return Whether the input has changed. + */ + public boolean setInput(ComputerSide side, int level, int bundledState) { + var index = side.ordinal(); + inputLock.lock(); + try { + var changed = false; + if (input[index] != level) { + input[index] = level; + changed = true; + } + + if (bundledInput[index] != bundledState) { + bundledInput[index] = bundledState; + changed = true; + } + + inputChanged |= changed; + return changed; + } finally { + inputLock.unlock(); + } + } + + /** + * Check whether any redstone inputs set by {@link #setInput(ComputerSide, int, int)} have changed since the last + * call to this function. + * + * @return Whether any redstone inputs has changed. + */ + public boolean pollInputChanged() { + var changed = inputChanged; + inputChanged = false; + return changed; + } +} diff --git a/projects/core/src/testFixtures/java/dan200/computercraft/test/core/apis/BasicApiEnvironment.java b/projects/core/src/testFixtures/java/dan200/computercraft/test/core/apis/BasicApiEnvironment.java index 9e9b76388a..dd75d2a32f 100644 --- a/projects/core/src/testFixtures/java/dan200/computercraft/test/core/apis/BasicApiEnvironment.java +++ b/projects/core/src/testFixtures/java/dan200/computercraft/test/core/apis/BasicApiEnvironment.java @@ -63,34 +63,6 @@ public void shutdown() { public void reboot() { } - @Override - public void setOutput(ComputerSide side, int output) { - } - - @Override - public int getOutput(ComputerSide side) { - return 0; - } - - @Override - public int getInput(ComputerSide side) { - return 0; - } - - @Override - public void setBundledOutput(ComputerSide side, int output) { - } - - @Override - public int getBundledOutput(ComputerSide side) { - return 0; - } - - @Override - public int getBundledInput(ComputerSide side) { - return 0; - } - @Override public void setPeripheralChangeListener(@Nullable IPeripheralChangeListener listener) { } diff --git a/projects/fabric/src/generated/resources/data/computercraft/recipes/redstone_relay.json b/projects/fabric/src/generated/resources/data/computercraft/recipes/redstone_relay.json new file mode 100644 index 0000000000..b3fbd01c1c --- /dev/null +++ b/projects/fabric/src/generated/resources/data/computercraft/recipes/redstone_relay.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": {"item": "computercraft:wired_modem"}, + "R": {"tag": "c:redstone_dusts"}, + "S": {"item": "minecraft:stone"} + }, + "pattern": ["SRS", "RCR", "SRS"], + "result": {"item": "computercraft:redstone_relay"}, + "show_notification": true +} diff --git a/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json b/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json new file mode 100644 index 0000000000..1e95e19adc --- /dev/null +++ b/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json @@ -0,0 +1 @@ +{"replace": false, "values": ["computercraft:lectern"]} diff --git a/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index 832d212710..ab90f21ff3 100644 --- a/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/projects/fabric/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -13,6 +13,7 @@ "computercraft:wireless_modem_normal", "computercraft:wireless_modem_advanced", "computercraft:wired_modem_full", - "computercraft:cable" + "computercraft:cable", + "computercraft:redstone_relay" ] } diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index b77d74388e..0980ac3ab5 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -79,6 +79,7 @@ public static void init() { PeripheralLookup.get().registerForBlockEntity(WirelessModemBlockEntity::getPeripheral, ModRegistry.BlockEntities.WIRELESS_MODEM_ADVANCED.get()); PeripheralLookup.get().registerForBlockEntity(WiredModemFullBlockEntity::getPeripheral, ModRegistry.BlockEntities.WIRED_MODEM_FULL.get()); PeripheralLookup.get().registerForBlockEntity(CableBlockEntity::getPeripheral, ModRegistry.BlockEntities.CABLE.get()); + PeripheralLookup.get().registerForBlockEntity((b, d) -> b.peripheral(), ModRegistry.BlockEntities.REDSTONE_RELAY.get()); WiredElementLookup.get().registerForBlockEntity((b, d) -> b.getElement(), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get()); WiredElementLookup.get().registerForBlockEntity(CableBlockEntity::getWiredElement, ModRegistry.BlockEntities.CABLE.get()); diff --git a/projects/forge/src/generated/resources/data/computercraft/recipes/redstone_relay.json b/projects/forge/src/generated/resources/data/computercraft/recipes/redstone_relay.json new file mode 100644 index 0000000000..df1c72b60d --- /dev/null +++ b/projects/forge/src/generated/resources/data/computercraft/recipes/redstone_relay.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": {"item": "computercraft:wired_modem"}, + "R": {"tag": "forge:dusts/redstone"}, + "S": {"item": "minecraft:stone"} + }, + "pattern": ["SRS", "RCR", "SRS"], + "result": {"item": "computercraft:redstone_relay"}, + "show_notification": true +} diff --git a/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json b/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json new file mode 100644 index 0000000000..6e36891c88 --- /dev/null +++ b/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/axe.json @@ -0,0 +1 @@ +{"values": ["computercraft:lectern"]} diff --git a/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index e5a4c9d201..7e4df4d4c5 100644 --- a/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/projects/forge/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -12,6 +12,7 @@ "computercraft:wireless_modem_normal", "computercraft:wireless_modem_advanced", "computercraft:wired_modem_full", - "computercraft:cable" + "computercraft:cable", + "computercraft:redstone_relay" ] } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java index eb03e9e6da..5c74b32b2f 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java @@ -18,6 +18,7 @@ import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity; import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; import dan200.computercraft.shared.peripheral.printer.PrinterBlockEntity; +import dan200.computercraft.shared.peripheral.redstone.RedstoneRelayBlockEntity; import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.util.CapabilityProvider; @@ -43,6 +44,7 @@ import net.minecraftforge.items.wrapper.SidedInvWrapper; import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; +import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT; import static net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER; /** @@ -133,15 +135,15 @@ public static void onCapability(AttachCapabilitiesEvent event) { CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(diskDrive)); CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, diskDrive::peripheral); } else if (blockEntity instanceof CableBlockEntity cable) { - var peripheralHandler = SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, cable::getPeripheral); - var elementHandler = SidedCapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, cable::getWiredElement); + var peripheralHandler = SidedCapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, cable::getPeripheral); + var elementHandler = SidedCapabilityProvider.attach(event, WIRED_ELEMENT, CAPABILITY_WIRED_ELEMENT, cable::getWiredElement); cable.onModemChanged(() -> { peripheralHandler.invalidate(); elementHandler.invalidate(); }); } else if (blockEntity instanceof WiredModemFullBlockEntity modem) { - SidedCapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, modem::getPeripheral); - CapabilityProvider.attach(event, WIRED_ELEMENT, Capabilities.CAPABILITY_WIRED_ELEMENT, modem::getElement); + SidedCapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, modem::getPeripheral); + CapabilityProvider.attach(event, WIRED_ELEMENT, CAPABILITY_WIRED_ELEMENT, modem::getElement); } else if (blockEntity instanceof WirelessModemBlockEntity modem) { var peripheral = SidedCapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, modem::getPeripheral); modem.onModemChanged(peripheral::invalidate); @@ -150,12 +152,14 @@ public static void onCapability(AttachCapabilitiesEvent event) { } else if (blockEntity instanceof SpeakerBlockEntity speaker) { CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, speaker::peripheral); } else if (blockEntity instanceof PrinterBlockEntity printer) { - CapabilityProvider.attach(event, PERIPHERAL, Capabilities.CAPABILITY_PERIPHERAL, printer::peripheral); + CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, printer::peripheral); // We don't need to invalidate here as the block's can't be rotated on the X axis! SidedCapabilityProvider.attach( event, INVENTORY, ITEM_HANDLER, s -> s == null ? new InvWrapper(printer) : new SidedInvWrapper(printer, s) ); + } else if (blockEntity instanceof RedstoneRelayBlockEntity redstone) { + CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, redstone::peripheral); } else if (Config.enableCommandBlock && blockEntity instanceof CommandBlockEntity commandBlock) { CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, () -> new CommandBlockPeripheral(commandBlock)); } diff --git a/projects/web/src/htmlTransform/export/index.json b/projects/web/src/htmlTransform/export/index.json index 90794b5d78..b44d6e582b 100644 --- a/projects/web/src/htmlTransform/export/index.json +++ b/projects/web/src/htmlTransform/export/index.json @@ -14,6 +14,7 @@ "computercraft:printed_page": "Printed Page", "computercraft:printed_pages": "Printed Pages", "computercraft:printer": "Printer", + "computercraft:redstone_relay": "Redstone Relay", "computercraft:speaker": "Speaker", "computercraft:treasure_disk": "Floppy Disk", "computercraft:turtle_advanced": "Advanced Turtle", @@ -48,6 +49,7 @@ "minecraft:purple_dye": "Purple Dye", "minecraft:red_dye": "Red Dye", "minecraft:redstone": "Redstone Dust", + "minecraft:stick": "Stick", "minecraft:stone": "Stone", "minecraft:string": "String", "minecraft:white_dye": "White Dye", @@ -90,7 +92,7 @@ ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], - ["computercraft:computer_advanced"], + ["computercraft:computer_normal"], ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], null, @@ -281,6 +283,21 @@ "output": "computercraft:printer", "count": 1 }, + "computercraft:redstone_relay": { + "inputs": [ + ["minecraft:stone"], + ["minecraft:redstone"], + ["minecraft:stone"], + ["minecraft:redstone"], + ["computercraft:wired_modem"], + ["minecraft:redstone"], + ["minecraft:stone"], + ["minecraft:redstone"], + ["minecraft:stone"] + ], + "output": "computercraft:redstone_relay", + "count": 1 + }, "computercraft:speaker": { "inputs": [ ["minecraft:stone"], @@ -311,13 +328,43 @@ "output": "computercraft:turtle_advanced", "count": 1 }, + "computercraft:turtle_advanced_overlays/turtle_rainbow_overlay": { + "inputs": [ + ["minecraft:red_dye"], + ["minecraft:orange_dye"], + ["minecraft:yellow_dye"], + ["minecraft:green_dye"], + ["minecraft:blue_dye"], + ["minecraft:purple_dye"], + ["minecraft:stick"], + ["computercraft:turtle_advanced"], + null + ], + "output": "computercraft:turtle_advanced", + "count": 1 + }, + "computercraft:turtle_advanced_overlays/turtle_trans_overlay": { + "inputs": [ + ["minecraft:light_blue_dye"], + ["minecraft:pink_dye"], + ["minecraft:white_dye"], + ["minecraft:stick"], + ["computercraft:turtle_advanced"], + null, + null, + null, + null + ], + "output": "computercraft:turtle_advanced", + "count": 1 + }, "computercraft:turtle_advanced_upgrade": { "inputs": [ ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], ["minecraft:gold_ingot"], - ["computercraft:computer_advanced"], + ["computercraft:turtle_normal"], ["minecraft:gold_ingot"], null, ["minecraft:gold_block"], @@ -341,6 +388,36 @@ "output": "computercraft:turtle_normal", "count": 1 }, + "computercraft:turtle_normal_overlays/turtle_rainbow_overlay": { + "inputs": [ + ["minecraft:red_dye"], + ["minecraft:orange_dye"], + ["minecraft:yellow_dye"], + ["minecraft:green_dye"], + ["minecraft:blue_dye"], + ["minecraft:purple_dye"], + ["minecraft:stick"], + ["computercraft:turtle_normal"], + null + ], + "output": "computercraft:turtle_normal", + "count": 1 + }, + "computercraft:turtle_normal_overlays/turtle_trans_overlay": { + "inputs": [ + ["minecraft:light_blue_dye"], + ["minecraft:pink_dye"], + ["minecraft:white_dye"], + ["minecraft:stick"], + ["computercraft:turtle_normal"], + null, + null, + null, + null + ], + "output": "computercraft:turtle_normal", + "count": 1 + }, "computercraft:wired_modem": { "inputs": [ ["minecraft:stone"], @@ -397,4 +474,4 @@ "count": 1 } } -} +} \ No newline at end of file diff --git a/projects/web/src/htmlTransform/export/items/computercraft/redstone_relay.png b/projects/web/src/htmlTransform/export/items/computercraft/redstone_relay.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4e6e7963b44f2c86b97f3319cb01b9532295ed GIT binary patch literal 1625 zcmXw33s93+7QTP}Bs`Qn2#FyRAcO!vf)EG~69kf&K+rUXfED8Egs=r17YG<1Si!^~ zudEh|x>!UKsfeypTLrPWZ4ipmStMA-qE#9m0?(R+^kvu#+ z$Ye5wLRqtB4V6HkQmLMvo-`VbPN#c$d3lqF-rn9mK0XWv!`IiB$z=NZ`LS3me}De~ z8Z96oATTfxL6Eg;*M@|ItYfi4Lqo&D!r0z)Hk%zD9v%@9!QpTsBO{}tqGDoVxLhue z$BRV}kw_#KixVM`n3yP$NH&EAZ{qP}GMQX1Pf1BpC=_V`N=r-o1VH)u`5KL;5CsYg z3yYLWtyWuHTzu%zp^}o4($Z4BUSA18%>eZk42B@o3V=Vo>qEBx6VfVVsenH`-xWMG+L@meU3jgJ==!) zvt0l%?5wL8adZ>1BZ0|%@A=_vF-&5YR$VBt%V+>BB~J$AUwl3HdEAEqyesR9gm2dl zj@s?^6FV7K9q)WKq1efh+Hf7t*#rqTVL#4cwnS}U$${zX&tS*w@O5(tL(b0{UuUn) z2bRzJk6pBu^CPSjq9m(n^QXWWyu7@}D9&YXeKYjQ7y6JB3ugxl0NV1=z`I?Dxtm$K zzx#B7ugG`o{4+0xF7I$L192{D&3de@EO0>vP8dsm9f6?)>sKTM)t_`Pzvk9*>@fr_ zKJPbc?mlXqIvgEwsglui`R&^+@qesa|J&0BG}A-bm$~)UPJ$-Sb;CIbKK3ehF$uzG zN5_sSeQl3K>WXA@IKe1&6_{ULdh;WuU1(|lr8I@M8$W3ZC$KS0x5x#<#-39*g0+i2 z+ZP&_AL*}@>!SrRXzPKX=iB~fHW@B1jc#t5F8BL6kmk>lTQ;wJ{1|J3DNo?Am9gi) z+mu<1aOaefiSe$zFVnwtLI34pJYKtDV)msAzN%cn;EqpDsQ;j}L_SvuEGP4tk=+OO zP5o%WVfB-73mF|W5_f&Nbu73{J(L&LWvi-)44ES0G>6%!s9X}yLV9acTw3xY@oHe34>NsM;7{;nr<4zR_W1< z8UAfF`qWY6uSHJz;n=mUYT5NZo|a8;Ma<{{02f8bkCeNx07$#wT79iba;_5m9hxzD zwx|NkTBDM;x15hr-LzPUHM$3_>hF@=4(0^S2*|b`A^D$VMrO0wHR)DDFZH*3OT_^h zXnKjQGyTf^Mgv;8agP2%lTKarF9QoToit0orp%mhG8xV&w?naSJ<_$t25w&~&3O4;!?P36A*Gwj;>8kOu|j_mT4+QtVpR}1eBBnC($JYg;WTv^aF&R*k# zvG{Q;=FT>~OU>0sJ>S&ymvNsZPo3XcLC5%ATCa$mH4(Sa(8vD_! z4LZ=K_@$;UzxM8~Z8yDsC`*X05xgJ<6TmiO9+32to<>V2SuS1cFSLh_NKdFPOpM*^M+H$I{cqF~s9|a)JaaOT(l|3mzQM3J5AHl#F0)KFE6P7|WN- u5eG9QdfGt5G!G614UYve%bS}R*%&U>YZk`%JW&A}!r literal 0 HcmV?d00001 diff --git a/projects/web/src/main/java/cc/tweaked/web/stub/ReentrantLock.java b/projects/web/src/main/java/cc/tweaked/web/stub/ReentrantLock.java index ebe15a2f75..28fe99018e 100644 --- a/projects/web/src/main/java/cc/tweaked/web/stub/ReentrantLock.java +++ b/projects/web/src/main/java/cc/tweaked/web/stub/ReentrantLock.java @@ -8,6 +8,9 @@ * A no-op stub for {@link java.util.concurrent.locks.ReentrantLock}. */ public class ReentrantLock { + public void lock() { + } + public boolean tryLock() { return true; }