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:
parent
f212861370
commit
03388149b1
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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}"
|
||||
]
|
||||
}
|
@ -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 = {}
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user