diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java index d69aac118..cb5170444 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlock.java @@ -129,7 +129,6 @@ public boolean onCustomDestroyBlock(BlockState state, Level world, BlockPos pos, world.setBlockAndUpdate(pos, correctConnections(world, pos, newState)); - cable.modemChanged(); cable.connectionsChanged(); if (!world.isClientSide && !player.getAbilities().instabuild) { Block.popResource(world, pos, item); @@ -162,10 +161,7 @@ public ItemStack getCloneItemStack(BlockState state, @Nullable HitResult hit, Bl @Override public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { var tile = world.getBlockEntity(pos); - if (tile instanceof CableBlockEntity cable) { - if (cable.hasCable()) cable.connectionsChanged(); - } - + if (tile instanceof CableBlockEntity cable && cable.hasCable()) cable.connectionsChanged(); super.setPlacedBy(world, pos, state, placer, stack); } @@ -177,14 +173,32 @@ public FluidState getFluidState(BlockState state) { @Override @Deprecated - public BlockState updateShape(BlockState state, Direction side, BlockState otherState, LevelAccessor world, BlockPos pos, BlockPos otherPos) { - WaterloggableHelpers.updateShape(state, world, pos); + public BlockState updateShape(BlockState state, Direction side, BlockState otherState, LevelAccessor level, BlockPos pos, BlockPos otherPos) { + WaterloggableHelpers.updateShape(state, level, pos); + // Should never happen, but handle the case where we've no modem or cable. if (!state.getValue(CABLE) && state.getValue(MODEM) == CableModemVariant.None) { return getFluidState(state).createLegacyBlock(); } - return world instanceof Level level ? state.setValue(CONNECTIONS.get(side), doesConnectVisually(state, level, pos, side)) : state; + // Pop our modem if needed. + var dir = state.getValue(MODEM).getFacing(); + if (dir != null && dir.equals(side) && !canSupportCenter(level, otherPos, side.getOpposite())) { + // If we've no cable, follow normal Minecraft logic and just remove the block. + if (!state.getValue(CABLE)) return getFluidState(state).createLegacyBlock(); + + // Otherwise remove the cable and drop the modem manually. + state = state.setValue(CableBlock.MODEM, CableModemVariant.None); + if (level instanceof Level actualLevel) { + Block.popResource(actualLevel, pos, new ItemStack(ModRegistry.Items.WIRED_MODEM.get())); + } + + if (level.getBlockEntity(pos) instanceof CableBlockEntity cable) cable.scheduleConnectionsChanged(); + } + + return level instanceof Level actualLevel + ? state.setValue(CONNECTIONS.get(side), doesConnectVisually(state, actualLevel, pos, side)) + : state; } @Override @@ -230,6 +244,7 @@ public static BlockState correctConnections(Level world, BlockPos pos, BlockStat @Override @Deprecated public final InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.isCrouching() || !player.mayBuild()) return InteractionResult.PASS; return world.getBlockEntity(pos) instanceof CableBlockEntity modem ? modem.use(player) : InteractionResult.PASS; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java index 2605eeae1..9ea7c4668 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockEntity.java @@ -7,7 +7,6 @@ import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredNode; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.command.text.ChatHelpers; import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.platform.ComponentAccess; @@ -20,9 +19,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -68,7 +65,8 @@ protected void detachPeripheral(String name) { ) { @Override public Vec3 getPosition() { - return Vec3.atCenterOf(getBlockPos().relative(getDirection())); + var dir = getModemDirection(); + return Vec3.atCenterOf(dir == null ? getBlockPos() : getBlockPos().relative(dir)); } }; @@ -95,45 +93,22 @@ public void clearRemoved() { @Override @Deprecated public void setBlockState(BlockState state) { - var direction = getMaybeDirection(); + var direction = getModemDirection(); super.setBlockState(state); // We invalidate both the modem and element if the modem's direction is different. - if (getMaybeDirection() != direction && modemChanged != null) modemChanged.run(); + if (getModemDirection() != direction && modemChanged != null) modemChanged.run(); } @Nullable - private Direction getMaybeDirection() { + private Direction getModemDirection() { return getBlockState().getValue(CableBlock.MODEM).getFacing(); } - private Direction getDirection() { - var direction = getMaybeDirection(); - return direction == null ? Direction.NORTH : direction; - } - void neighborChanged(BlockPos neighbour) { - var dir = getDirection(); - if (neighbour.equals(getBlockPos().relative(dir)) && hasModem() && !getBlockState().canSurvive(getLevel(), getBlockPos())) { - if (hasCable()) { - // Drop the modem and convert to cable - Block.popResource(getLevel(), getBlockPos(), new ItemStack(ModRegistry.Items.WIRED_MODEM.get())); - getLevel().setBlockAndUpdate(getBlockPos(), getBlockState().setValue(CableBlock.MODEM, CableModemVariant.None)); - modemChanged(); - connectionsChanged(); - } else { - // Drop everything and remove block - Block.popResource(getLevel(), getBlockPos(), new ItemStack(ModRegistry.Items.WIRED_MODEM.get())); - getLevel().removeBlock(getBlockPos(), false); - // This'll call #destroy(), so we don't need to reset the network here. - } - - return; - } - - if (!level.isClientSide && isPeripheralOn()) { - var facing = getDirection(); - if (getBlockPos().relative(facing).equals(neighbour)) queueRefreshPeripheral(); + var dir = getModemDirection(); + if (!level.isClientSide && dir != null && getBlockPos().relative(dir).equals(neighbour) && isPeripheralOn()) { + queueRefreshPeripheral(); } } @@ -143,7 +118,6 @@ private void queueRefreshPeripheral() { } InteractionResult use(Player player) { - if (player.isCrouching() || !player.mayBuild()) return InteractionResult.PASS; if (!canAttachPeripheral()) return InteractionResult.FAIL; if (getLevel().isClientSide) return InteractionResult.SUCCESS; @@ -206,7 +180,7 @@ void blockTick() { if (refreshConnections) connectionsChanged(); } - private void scheduleConnectionsChanged() { + void scheduleConnectionsChanged() { refreshConnections = true; TickScheduler.schedule(tickToken); } @@ -234,20 +208,14 @@ void connectionsChanged() { this.node.disconnectFrom(node); } } - } - - void modemChanged() { - // Tell anyone who cares that the connection state has changed - if (modemChanged != null) modemChanged.run(); - - if (getLevel().isClientSide) return; // If we can no longer attach peripherals, then detach any which may have existed if (!canAttachPeripheral()) detachPeripheral(); } private void attachPeripheral() { - if (peripheral.attach(getLevel(), getBlockPos(), getDirection())) updateConnectedPeripherals(); + var dir = Objects.requireNonNull(getModemDirection(), "Attaching without a modem"); + if (peripheral.attach(getLevel(), getBlockPos(), dir)) updateConnectedPeripherals(); updateBlockState(); } @@ -267,7 +235,7 @@ public WiredElement getWiredElement(@Nullable Direction direction) { @Nullable public IPeripheral getPeripheral(@Nullable Direction direction) { - return direction == null || getMaybeDirection() == direction ? modem : null; + return direction == null || getModemDirection() == direction ? modem : null; } private boolean isPeripheralOn() { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java index a5414c0f0..7149f01ee 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableBlockItem.java @@ -35,10 +35,7 @@ boolean placeAt(Level world, BlockPos pos, BlockState state) { world.playSound(null, pos, soundType.getPlaceSound(), SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F); var tile = world.getBlockEntity(pos); - if (tile instanceof CableBlockEntity cable) { - cable.modemChanged(); - cable.connectionsChanged(); - } + if (tile instanceof CableBlockEntity cable) cable.connectionsChanged(); return true; } diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt index ccbf73e42..85387ee76 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt @@ -7,19 +7,18 @@ import dan200.computercraft.api.lua.ObjectArguments import dan200.computercraft.core.apis.PeripheralAPI import dan200.computercraft.core.computer.ComputerSide -import dan200.computercraft.gametest.api.getBlockEntity -import dan200.computercraft.gametest.api.sequence -import dan200.computercraft.gametest.api.thenOnComputer -import dan200.computercraft.gametest.api.thenStartComputer +import dan200.computercraft.gametest.api.* import dan200.computercraft.impl.network.wired.WiredNodeImpl import dan200.computercraft.shared.ModRegistry import dan200.computercraft.shared.peripheral.modem.wired.CableBlock +import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant import dan200.computercraft.test.core.assertArrayEquals import dan200.computercraft.test.core.computer.LuaTaskContext import dan200.computercraft.test.core.computer.getApi 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 org.junit.jupiter.api.Assertions.assertEquals import kotlin.time.Duration.Companion.milliseconds @@ -125,6 +124,32 @@ fun Full_block_modem_does_not_report_self(helper: GameTestHelper) = helper.seque thenIdle(2) thenOnComputer { assertEquals(listOf("back", "computer_1", "right"), getPeripheralNames()) } } + + /** + * Test wired modems (without a cable) drop an item when the adjacent block is removed. + */ + @GameTest + fun Modem_drops_when_neighbour_removed(helper: GameTestHelper) = helper.sequence { + thenExecute { + helper.setBlock(BlockPos(2, 3, 2), Blocks.AIR) + helper.assertItemEntityPresent(ModRegistry.Items.WIRED_MODEM.get(), BlockPos(2, 2, 2), 0.0) + helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2)) + } + } + + /** + * Test wired modems (with a cable) drop an item, but keep their cable when the adjacent block is removed. + */ + @GameTest + fun Modem_keeps_cable_when_neighbour_removed(helper: GameTestHelper) = helper.sequence { + thenExecute { + helper.setBlock(BlockPos(2, 3, 2), Blocks.AIR) + helper.assertItemEntityPresent(ModRegistry.Items.WIRED_MODEM.get(), BlockPos(2, 2, 2), 0.0) + helper.assertBlockIs(BlockPos(2, 2, 2)) { + it.block == ModRegistry.Blocks.CABLE.get() && it.getValue(CableBlock.MODEM) == CableModemVariant.None && it.getValue(CableBlock.CABLE) + } + } + } } private fun LuaTaskContext.findPeripheral(type: String): String? { diff --git a/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_drops_when_neighbour_removed.snbt b/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_drops_when_neighbour_removed.snbt new file mode 100644 index 000000000..3007188a3 --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_drops_when_neighbour_removed.snbt @@ -0,0 +1,138 @@ +{ + DataVersion: 3465, + 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:light_gray_stained_glass"}, + {pos: [1, 1, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 1, 2], state: "computercraft:cable{cable:false,down:false,east:false,modem:up_off,north:false,south:false,up:false,waterlogged:false,west:false}", nbt: {id: "computercraft:cable"}}, + {pos: [2, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 1, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {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:light_gray_stained_glass"}, + {pos: [1, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {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:light_gray_stained_glass", + "minecraft:air", + "computercraft:cable{cable:false,down:false,east:false,modem:up_off,north:false,south:false,up:false,waterlogged:false,west:false}" + ] +} diff --git a/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_keeps_cable_when_neighbour_removed.snbt b/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_keeps_cable_when_neighbour_removed.snbt new file mode 100644 index 000000000..be462dd2c --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/modem_test.modem_keeps_cable_when_neighbour_removed.snbt @@ -0,0 +1,138 @@ +{ + DataVersion: 3465, + 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:light_gray_stained_glass"}, + {pos: [1, 1, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:up_off,north:false,south:false,up:true,waterlogged:false,west:false}", nbt: {id: "computercraft:cable"}}, + {pos: [2, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 1, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 1, 3], state: "minecraft:light_gray_stained_glass"}, + {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:light_gray_stained_glass"}, + {pos: [1, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 2, 2], state: "minecraft:light_gray_stained_glass"}, + {pos: [3, 2, 3], state: "minecraft:light_gray_stained_glass"}, + {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:light_gray_stained_glass", + "minecraft:air", + "computercraft:cable{cable:true,down:false,east:false,modem:up_off,north:false,south:false,up:true,waterlogged:false,west:false}" + ] +}