1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-02-22 14:00:09 +00:00

Fix command computers being exposed as peripherals

- Check whether the computer is a command computer before registering
   the capability.

 - Add tests to check what is/isn't a peripheral. See also #2020, where
   we forgot to register a peripheral on NeoForge 1.21.1.

Fixes #2070.
This commit is contained in:
Jonathan Coates 2025-01-26 11:13:52 +00:00
parent f212861370
commit 03388149b1
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
8 changed files with 93 additions and 160 deletions

View File

@ -29,6 +29,7 @@ import net.minecraft.world.LockCode;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Nameable;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.GameMasterBlock;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
@ -310,6 +311,10 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
return label;
}
public final boolean isAdminOnly() {
return getBlockState().getBlock() instanceof GameMasterBlock;
}
public final void setComputerID(int id) {
if (getLevel().isClientSide || computerID == id) return;
@ -420,6 +425,6 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
@Override
public boolean onlyOpCanSetNbt() {
return getFamily() == ComputerFamily.COMMAND;
return isAdminOnly();
}
}

View File

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.gametest
import dan200.computercraft.gametest.api.assertNoPeripheral
import dan200.computercraft.gametest.api.assertPeripheral
import dan200.computercraft.gametest.api.immediate
import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.platform.ComponentAccess
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
import java.util.*
/**
* Checks that we expose [ComponentAccess] for various blocks/block entities
*/
class Component_Test {
@GameTest(template = "default")
fun Peripheral(context: GameTestHelper) = context.immediate {
val pos = BlockPos(2, 2, 2)
// We fetch peripherals from the NORTH, as that is the default direction for modems. This is a bit of a hack,
// but avoids having to override the block state.
val side = Direction.NORTH
for ((block, type) in mapOf(
// Computers
ModRegistry.Blocks.COMPUTER_NORMAL to Optional.of("computer"),
ModRegistry.Blocks.COMPUTER_ADVANCED to Optional.of("computer"),
ModRegistry.Blocks.COMPUTER_COMMAND to Optional.empty(),
// Turtles
ModRegistry.Blocks.TURTLE_NORMAL to Optional.of("turtle"),
ModRegistry.Blocks.TURTLE_ADVANCED to Optional.of("turtle"),
// Peripherals
ModRegistry.Blocks.SPEAKER to Optional.of("speaker"),
ModRegistry.Blocks.DISK_DRIVE to Optional.of("drive"),
ModRegistry.Blocks.PRINTER to Optional.of("printer"),
ModRegistry.Blocks.MONITOR_NORMAL to Optional.of("monitor"),
ModRegistry.Blocks.MONITOR_ADVANCED to Optional.of("monitor"),
ModRegistry.Blocks.WIRELESS_MODEM_NORMAL to Optional.of("modem"),
ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED to Optional.of("modem"),
ModRegistry.Blocks.WIRED_MODEM_FULL to Optional.of("modem"),
ModRegistry.Blocks.REDSTONE_RELAY to Optional.of("redstone_relay"),
)) {
context.setBlock(pos, block.get())
if (type.isPresent) {
context.assertPeripheral(pos, side, type.get())
} else {
context.assertNoPeripheral(pos, side)
}
}
}
}

View File

@ -95,17 +95,6 @@ class Computer_Test {
}
}
/**
* Check computers and turtles expose peripherals.
*/
@GameTest
fun Computer_peripheral(context: GameTestHelper) = context.sequence {
thenExecute {
context.assertPeripheral(BlockPos(3, 2, 2), type = "computer")
context.assertPeripheral(BlockPos(1, 2, 2), type = "turtle")
}
}
/**
* Check chest peripherals are reattached with a new size.
*/

View File

@ -127,6 +127,14 @@ fun GameTestHelper.sequence(run: GameTestSequence.() -> Unit) {
sequence.thenSucceed()
}
/**
* Run a function immediately, and then succeed.
*/
fun GameTestHelper.immediate(run: () -> Unit) {
run()
succeed()
}
/**
* A custom instance of [GameTestAssertPosException] which allows for longer error messages.
*/
@ -232,15 +240,17 @@ private fun GameTestHelper.getPeripheralAt(pos: BlockPos, direction: Direction):
fun GameTestHelper.assertPeripheral(pos: BlockPos, direction: Direction = Direction.UP, type: String) {
val peripheral = getPeripheralAt(pos, direction)
val block = getBlockState(pos).block.name.string
when {
peripheral == null -> fail("No peripheral at position", pos)
peripheral.type != type -> fail("Peripheral is of type ${peripheral.type}, expected $type", pos)
peripheral == null -> fail("No peripheral for '$block'", pos)
peripheral.type != type -> fail("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
}
}
fun GameTestHelper.assertNoPeripheral(pos: BlockPos, direction: Direction = Direction.UP) {
val peripheral = getPeripheralAt(pos, direction)
if (peripheral != null) fail("Expected no peripheral, got a ${peripheral.type}", pos)
val block = getBlockState(pos).block.name
if (peripheral != null) fail("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
}
fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: String? = null) {

View File

@ -88,6 +88,7 @@ object TestHooks {
fun areComputersIdle(server: MinecraftServer) = ComputerThreadReflection.isFullyIdle(ServerContext.get(server))
private val testClasses = listOf(
Component_Test::class.java,
Computer_Test::class.java,
CraftOs_Test::class.java,
Disk_Drive_Test::class.java,

View File

@ -1,138 +0,0 @@
{
DataVersion: 2975,
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: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}},
{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: "minecraft:air"},
{pos: [2, 1, 3], state: "minecraft:air"},
{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: "computercraft:computer_advanced{facing:north,state:off}", nbt: {On: 0b, id: "computercraft:computer_advanced"}},
{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:turtle_advanced{facing:south,waterlogged:false}",
"computercraft:computer_advanced{facing:north,state:off}"
]
}

View File

@ -47,12 +47,22 @@ local function sortCoords(startX, startY, endX, endY)
return minX, maxX, minY, maxY
end
--- Parses an image from a multi-line string
--
-- @tparam string image The string containing the raw-image data.
-- @treturn table The parsed image data, suitable for use with
-- [`paintutils.drawImage`].
-- @since 1.80pr1
--[=[- Parses an image from a multi-line string
@tparam string image The string containing the raw-image data.
@treturn table The parsed image data, suitable for use with [`paintutils.drawImage`].
@usage Parse an image from a string, and draw it.
local image = paintutils.parseImage([[
e e
e e
eeee
]])
paintutils.drawImage(image, term.getCursorPos())
@since 1.80pr1
]=]
function parseImage(image)
expect(1, image, "string")
local tImage = {}

View File

@ -124,7 +124,7 @@ public class ForgeCommonHooks {
@SubscribeEvent
public static void onCapability(AttachCapabilitiesEvent<BlockEntity> event) {
var blockEntity = event.getObject();
if (blockEntity instanceof ComputerBlockEntity computer) {
if (blockEntity instanceof ComputerBlockEntity computer && !computer.isAdminOnly()) {
CapabilityProvider.attach(event, PERIPHERAL, CAPABILITY_PERIPHERAL, computer::peripheral);
} else if (blockEntity instanceof TurtleBlockEntity turtle) {
CapabilityProvider.attach(event, INVENTORY, ITEM_HANDLER, () -> new InvWrapper(turtle));