1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-03-18 01:18:12 +00:00

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".
This commit is contained in:
Jonathan Coates 2024-11-12 09:05:27 +00:00 committed by GitHub
parent ba6da3bc6c
commit 4f66ac79d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 1610 additions and 353 deletions

View File

@ -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`]

View File

@ -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"

View File

@ -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}
}
}

View File

@ -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",

View File

@ -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"
}
}

View File

@ -0,0 +1 @@
{"parent": "computercraft:block/redstone_relay"}

View File

@ -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
}

View File

@ -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"
}

View File

@ -97,6 +97,8 @@ class BlockModelProvider {
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 @@ class BlockModelProvider {
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 };

View File

@ -80,6 +80,7 @@ public final class LanguageProvider implements DataProvider {
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");

View File

@ -51,6 +51,7 @@ class LootTableProvider {
selfDrop(add, ModRegistry.Blocks.WIRED_MODEM_FULL);
selfDrop(add, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL);
selfDrop(add, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED);
selfDrop(add, ModRegistry.Blocks.REDSTONE_RELAY);
computerDrop(add, ModRegistry.Blocks.COMPUTER_NORMAL);
computerDrop(add, ModRegistry.Blocks.COMPUTER_ADVANCED);

View File

@ -461,6 +461,17 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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) {

View File

@ -78,9 +78,12 @@ class TagProvider {
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(

View File

@ -62,6 +62,8 @@ import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
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 @@ public final class ModRegistry {
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 @@ public final class ModRegistry {
}
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> 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<ComputerBlock<ComputerBlockEntity>> 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<ComputerBlock<ComputerBlockEntity>> 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<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), BlockEntities.TURTLE_NORMAL));
@ -179,6 +181,9 @@ public final class ModRegistry {
public static final RegistryEntry<CustomLecternBlock> 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<RedstoneRelayBlock> REDSTONE_RELAY = REGISTRY.register("redstone_relay",
() -> new RedstoneRelayBlock(redstoneConductor().mapColor(MapColor.STONE)));
}
public static class BlockEntities {
@ -222,6 +227,8 @@ public final class ModRegistry {
ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, (p, s) -> new WirelessModemBlockEntity(BlockEntities.WIRELESS_MODEM_ADVANCED.get(), p, s, true));
public static final RegistryEntry<BlockEntityType<CustomLecternBlockEntity>> LECTERN = ofBlock(Blocks.LECTERN, CustomLecternBlockEntity::new);
public static final RegistryEntry<BlockEntityType<RedstoneRelayBlockEntity>> REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, RedstoneRelayBlockEntity::new);
}
public static final class Items {
@ -267,6 +274,7 @@ public final class ModRegistry {
public static final RegistryEntry<BlockItem> WIRELESS_MODEM_NORMAL = ofBlock(Blocks.WIRELESS_MODEM_NORMAL, BlockItem::new);
public static final RegistryEntry<BlockItem> WIRELESS_MODEM_ADVANCED = ofBlock(Blocks.WIRELESS_MODEM_ADVANCED, BlockItem::new);
public static final RegistryEntry<BlockItem> WIRED_MODEM_FULL = ofBlock(Blocks.WIRED_MODEM_FULL, BlockItem::new);
public static final RegistryEntry<BlockItem> REDSTONE_RELAY = ofBlock(Blocks.REDSTONE_RELAY, BlockItem::new);
public static final RegistryEntry<CableBlockItem.Cable> CABLE = REGISTRY.register("cable",
() -> new CableBlockItem.Cable(Blocks.CABLE.get(), properties()));
@ -415,6 +423,7 @@ public final class ModRegistry {
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());

View File

@ -127,7 +127,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
// 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 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
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)
);
}
/**

View File

@ -134,8 +134,8 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
*
* @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 class ServerComputer implements InputHandler, ComputerEnvironment {
}
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) {

View File

@ -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<Block, BlockState> 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);
}
}

View File

@ -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;
}
}

View File

@ -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.
* <p>
* 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").
*
* <p>
* ## Recipe
* <div class="recipe-container">
* <mc-recipe recipe="computercraft:redstone_relay"></mc-recipe>
* </div>
*
* @cc.usage Toggle the redstone signal above the computer every 0.5 seconds.
*
* <pre>{@code
* local relay = peripheral.find("redstone_relay")
* while true do
* relay.setOutput("top", not relay.getOutput("top"))
* sleep(0.5)
* end
* }</pre>
* @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");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

View File

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.gametest
import dan200.computercraft.core.computer.ComputerSide
import dan200.computercraft.gametest.api.assertBlockHas
import dan200.computercraft.gametest.api.getBlockEntity
import dan200.computercraft.gametest.api.modifyBlock
import dan200.computercraft.gametest.api.sequence
import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.peripheral.redstone.RedstoneRelayPeripheral
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.LeverBlock
import net.minecraft.world.level.block.RedstoneLampBlock
import org.junit.jupiter.api.Assertions.assertEquals
class Relay_Test {
/**
* Ensures redstone signals do not travel through relay.
*
* @see [Computer_Test.No_through_signal]
*/
@GameTest
fun No_through_signal(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") }
}
/**
* 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") }
}
}

View File

@ -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,
)

View File

@ -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}"
]
}

View File

@ -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}"
]
}

View File

@ -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}"
]
}

View File

@ -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}"
]
}

View File

@ -43,18 +43,6 @@ public interface IAPIEnvironment {
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

View File

@ -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 @@ import java.util.List;
* 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 class RedstoneAPI implements ILuaAPI {
public final List<String> 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.
* <pre>{@code
* print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
* }</pre>
* @see #getBundledInput
*/
@LuaFunction
public final boolean testBundledInput(ComputerSide side, int mask) {
var input = environment.getBundledInput(side);
return (input & mask) == mask;
}
}

View File

@ -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.
* <pre>{@code
* print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
* }</pre>
* @see #getBundledInput
*/
@LuaFunction
public final boolean testBundledInput(ComputerSide side, int mask) {
var input = redstone.getBundledInput(side);
return (input & mask) == mask;
}
}

View File

@ -12,10 +12,10 @@ import dan200.computercraft.core.ComputerContext;
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 class Computer {
return internalEnvironment;
}
public RedstoneState getRedstone() {
return redstone;
}
public IAPIEnvironment getAPIEnvironment() {
return internalEnvironment;
}
@ -157,10 +160,8 @@ public class Computer {
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 class Computer {
*
* @return What sides on the computer have changed.
*/
public int pollAndResetChanges() {
return externalOutputChanges.getAndSet(0);
public int pollRedstoneChanges() {
return redstone.updateOutput();
}
public boolean isBlinking() {

View File

@ -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 @@ final class ComputerExecutor implements ComputerScheduler.Worker {
// 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();
}

View File

@ -16,24 +16,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
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.
* <p>
* This handles storing and updating of peripherals and redstone.
*
* <h1>Redstone</h1>
* We holds three kinds of arrays for redstone, in normal and bundled versions:
* <ul>
* <li>{@link #internalOutput} is the redstone output which the computer has currently set. This is read on both
* threads, and written on the computer thread.</li>
* <li>{@link #externalOutput} is the redstone output currently propagated to the world. This is only read and written
* on the main thread.</li>
* <li>{@link #input} is the redstone input from external sources. This is read on both threads, and written on the main
* thread.</li>
* </ul>
* This handles storing and updating of peripherals and timers.
*
* <h1>Peripheral</h1>
* 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 final class Environment implements IAPIEnvironment {
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 @@ public final class Environment implements IAPIEnvironment {
* 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<Int2ObjectMap.Entry<Timer>> it = timers.int2ObjectEntrySet().iterator();
@ -218,45 +120,6 @@ public final class Environment implements IAPIEnvironment {
}
}
/**
* 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) {

View File

@ -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);
}

View File

@ -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).
* <p>
* 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.
*
* <h2>Input</h2>
* 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)}.
* <p>
* 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.
*
* <h2>Output</h2>
* 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 <em>internal</em> 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;
}
}

View File

@ -63,34 +63,6 @@ public abstract class BasicApiEnvironment implements IAPIEnvironment {
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) {
}

View File

@ -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
}

View File

@ -0,0 +1 @@
{"replace": false, "values": ["computercraft:lectern"]}

View File

@ -13,6 +13,7 @@
"computercraft:wireless_modem_normal",
"computercraft:wireless_modem_advanced",
"computercraft:wired_modem_full",
"computercraft:cable"
"computercraft:cable",
"computercraft:redstone_relay"
]
}

View File

@ -79,6 +79,7 @@ public class ComputerCraft {
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());

View File

@ -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
}

View File

@ -0,0 +1 @@
{"values": ["computercraft:lectern"]}

View File

@ -12,6 +12,7 @@
"computercraft:wireless_modem_normal",
"computercraft:wireless_modem_advanced",
"computercraft:wired_modem_full",
"computercraft:cable"
"computercraft:cable",
"computercraft:redstone_relay"
]
}

View File

@ -18,6 +18,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEnt
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.InvWrapper;
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 class ForgeCommonHooks {
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 class ForgeCommonHooks {
} 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));
}

View File

@ -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
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

View File

@ -8,6 +8,9 @@ package cc.tweaked.web.stub;
* A no-op stub for {@link java.util.concurrent.locks.ReentrantLock}.
*/
public class ReentrantLock {
public void lock() {
}
public boolean tryLock() {
return true;
}