From f21286137092a54297922f0e43732a3fe994661e Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 21 Jan 2025 20:41:55 +0000 Subject: [PATCH 1/7] Mark command computers as onlyOpCanSetNbt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't required in vanilla, as the command computer is a GameMasterBlock, and so isn't placeable in the first place. *However*, this is a problem with Create contraptions — with those it's possible to "place" a command computer complete with NBT. We override onlyOpCanSetNbt to prevent this [^1]. [^1]: https://github.com/Creators-of-Create/Create/blob/7a7993deb89e432f8a4cce8a8d5f35f85f57bfed/src/main/java/com/simibubi/create/foundation/utility/NBTProcessors.java#L179 --- .../shared/computer/blocks/AbstractComputerBlockEntity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java index aaceb04ee..472180b33 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java @@ -417,4 +417,9 @@ public Component getCustomName() { public Component getDisplayName() { return Nameable.super.getDisplayName(); } + + @Override + public boolean onlyOpCanSetNbt() { + return getFamily() == ComputerFamily.COMMAND; + } } From 03388149b11c01fb49b84da183effde9d50ca3f8 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 26 Jan 2025 11:13:52 +0000 Subject: [PATCH 2/7] 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. --- .../blocks/AbstractComputerBlockEntity.java | 7 +- .../computercraft/gametest/Component_Test.kt | 56 +++++++ .../computercraft/gametest/Computer_Test.kt | 11 -- .../gametest/api/TestExtensions.kt | 16 +- .../computercraft/gametest/core/TestHooks.kt | 1 + .../computer_test.computer_peripheral.snbt | 138 ------------------ .../computercraft/lua/rom/apis/paintutils.lua | 22 ++- .../shared/ForgeCommonHooks.java | 2 +- 8 files changed, 93 insertions(+), 160 deletions(-) create mode 100644 projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Component_Test.kt delete mode 100644 projects/common/src/testMod/resources/data/cctest/structures/computer_test.computer_peripheral.snbt diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java index 472180b33..73dabe123 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java @@ -29,6 +29,7 @@ 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 final int getComputerID() { 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 Component getDisplayName() { @Override public boolean onlyOpCanSetNbt() { - return getFamily() == ComputerFamily.COMMAND; + return isAdminOnly(); } } diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Component_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Component_Test.kt new file mode 100644 index 000000000..5ec3017d7 --- /dev/null +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Component_Test.kt @@ -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) + } + } + } +} diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt index 7a21696de..49dd54604 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt @@ -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. */ diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt index 4191ecb03..de0c2e0f5 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt @@ -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) { diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt index da5bf7f18..b1510719e 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt @@ -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, diff --git a/projects/common/src/testMod/resources/data/cctest/structures/computer_test.computer_peripheral.snbt b/projects/common/src/testMod/resources/data/cctest/structures/computer_test.computer_peripheral.snbt deleted file mode 100644 index 9a22ee331..000000000 --- a/projects/common/src/testMod/resources/data/cctest/structures/computer_test.computer_peripheral.snbt +++ /dev/null @@ -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}" - ] -} diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua index 567043223..013a51fed 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua @@ -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 = {} diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java index 5c74b32b2..9f6f5f264 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java @@ -124,7 +124,7 @@ public static void onDatapackSync(OnDatapackSyncEvent event) { @SubscribeEvent public static void onCapability(AttachCapabilitiesEvent 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)); From fa2140d00ba667902d361b4873804dd97dde3a19 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 26 Jan 2025 14:56:09 +0000 Subject: [PATCH 3/7] Clean up container data interface - Remove ContainerData.open. - Change PlatformHelper.openMenu to take a separate display name and MenuConstructor, rather than a MenuProvider. This makes the interface slightly easier to use in the common case, where we want to use lambdas instead. --- .../shared/command/CommandComputerCraft.java | 21 ++----- .../blocks/AbstractComputerBlock.java | 3 +- .../blocks/AbstractComputerBlockEntity.java | 4 +- .../network/container/ContainerData.java | 12 ---- .../shared/platform/PlatformHelper.java | 14 +++-- .../inventory/PocketComputerMenuProvider.java | 56 ------------------- .../pocket/items/PocketComputerItem.java | 13 ++++- .../computercraft/TestPlatformHelper.java | 5 +- .../shared/platform/PlatformHelperImpl.java | 15 ++--- .../shared/platform/PlatformHelperImpl.java | 6 +- 10 files changed, 45 insertions(+), 104 deletions(-) delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java diff --git a/projects/common/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/projects/common/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index ba0009ddb..4889f20f2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -24,15 +24,13 @@ import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver; import dan200.computercraft.shared.computer.metrics.basic.ComputerMetrics; import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.platform.PlatformHelper; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -260,18 +258,11 @@ private static int queue(Collection computers, List args * @return The constant {@code 1}. */ private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException { - var player = source.getPlayerOrException(); - new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() { - @Override - public Component getDisplayName() { - return Component.translatable("gui.computercraft.view_computer"); - } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) { - return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer); - } - }); + PlatformHelper.get().openMenu( + source.getPlayerOrException(), Component.translatable("gui.computercraft.view_computer"), + (id, player, entity) -> new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer), + new ComputerContainerData(computer, ItemStack.EMPTY) + ); return 1; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java index b9e61a7aa..4e093af11 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java @@ -9,6 +9,7 @@ import dan200.computercraft.shared.common.IBundledRedstoneBlock; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.RegistryEntry; import dan200.computercraft.shared.util.BlockEntityHelpers; import net.minecraft.core.BlockPos; @@ -161,7 +162,7 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player var serverComputer = computer.createServerComputer(); serverComputer.turnOn(); - new ComputerContainerData(serverComputer, getItem(computer)).open(player, computer); + PlatformHelper.get().openMenu(player, computer.getName(), computer, new ComputerContainerData(serverComputer, getItem(computer))); } return InteractionResult.sidedSuccess(level.isClientSide); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java index 73dabe123..cd7609460 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlockEntity.java @@ -26,9 +26,9 @@ import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.Container; 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.inventory.MenuConstructor; import net.minecraft.world.level.block.GameMasterBlock; import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; @@ -39,7 +39,7 @@ import java.util.Objects; import java.util.UUID; -public abstract class AbstractComputerBlockEntity extends BlockEntity implements Nameable, MenuProvider { +public abstract class AbstractComputerBlockEntity extends BlockEntity implements Nameable, MenuConstructor { private static final String NBT_ID = "ComputerId"; private static final String NBT_LABEL = "Label"; private static final String NBT_ON = "On"; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java b/projects/common/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java index 8ad6cd68d..1361ef901 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java @@ -7,9 +7,7 @@ import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.platform.PlatformHelper; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; @@ -21,16 +19,6 @@ public interface ContainerData { void toBytes(FriendlyByteBuf buf); - /** - * Open a menu for a specific player using this data. - * - * @param player The player to open the menu for. - * @param menu The underlying menu provider. - */ - default void open(Player player, MenuProvider menu) { - PlatformHelper.get().openMenu(player, menu, this); - } - static MenuType toType(Function reader, Factory factory) { return PlatformHelper.get().createMenuType(reader, factory); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java index a0553c6bb..d24eff2ca 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java @@ -20,6 +20,7 @@ import net.minecraft.core.Direction; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceKey; @@ -29,11 +30,15 @@ import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.tags.TagKey; -import net.minecraft.world.*; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.WorldlyContainer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.MenuConstructor; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.DyeColor; @@ -159,10 +164,11 @@ static PlatformHelper get() { * Open a container using a specific {@link ContainerData}. * * @param player The player to open the menu for. - * @param owner The underlying menu provider. - * @param menu The menu data. + * @param title The title for this menu. + * @param menu The underlying menu constructor. + * @param data The menu data. */ - void openMenu(Player player, MenuProvider owner, ContainerData menu); + void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data); /** * Create a new {@link MessageType}. diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java deleted file mode 100644 index 5c091afb1..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.pocket.inventory; - -import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; -import dan200.computercraft.shared.pocket.items.PocketComputerItem; -import net.minecraft.network.chat.Component; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.item.ItemStack; - -import javax.annotation.Nullable; - -import static dan200.computercraft.core.util.Nullability.assertNonNull; - -public class PocketComputerMenuProvider implements MenuProvider { - private final ServerComputer computer; - private final Component name; - private final PocketComputerItem item; - private final InteractionHand hand; - private final boolean isTypingOnly; - - public PocketComputerMenuProvider(ServerComputer computer, ItemStack stack, PocketComputerItem item, InteractionHand hand, boolean isTypingOnly) { - this.computer = computer; - name = stack.getHoverName(); - this.item = item; - this.hand = hand; - this.isTypingOnly = isTypingOnly; - } - - - @Override - public Component getDisplayName() { - return name; - } - - @Nullable - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player entity) { - return new ComputerMenuWithoutInventory( - isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), id, inventory, - p -> { - var stack = p.getItemInHand(hand); - return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer; - }, - computer - ); - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java index 9e54e2b3e..fd557fc58 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java @@ -12,18 +12,20 @@ import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.impl.PocketUpgrades; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.computer.core.ServerContext; +import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.pocket.core.PocketBrain; import dan200.computercraft.shared.pocket.core.PocketHolder; import dan200.computercraft.shared.pocket.core.PocketServerComputer; -import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider; import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.NBTUtil; @@ -176,8 +178,13 @@ public InteractionResultHolder use(Level world, Player player, Intera } if (!stop) { - var isTypingOnly = hand == InteractionHand.OFF_HAND; - new ComputerContainerData(computer, stack).open(player, new PocketComputerMenuProvider(computer, stack, this, hand, isTypingOnly)); + PlatformHelper.get().openMenu( + player, stack.getHoverName(), + (id, inventory, entity) -> new ComputerMenuWithoutInventory( + hand == InteractionHand.OFF_HAND ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), + id, inventory, p -> isServerComputer(computer, p.getItemInHand(hand)), computer + ), + new ComputerContainerData(computer, stack)); } } return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack); diff --git a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java index 879e996d0..bd7f412cf 100644 --- a/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java +++ b/projects/common/src/test/java/dan200/computercraft/TestPlatformHelper.java @@ -25,6 +25,7 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; @@ -35,11 +36,11 @@ import net.minecraft.tags.TagKey; import net.minecraft.world.Container; import net.minecraft.world.InteractionResult; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.MenuConstructor; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; @@ -140,7 +141,7 @@ public MenuType cr } @Override - public void openMenu(Player player, MenuProvider owner, ContainerData menu) { + public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) { throw new UnsupportedOperationException("Cannot open menu inside tests"); } diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 72bccefb7..11690ae41 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -59,12 +59,12 @@ import net.minecraft.world.Container; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.MenuConstructor; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.*; import net.minecraft.world.item.crafting.Ingredient; @@ -170,8 +170,8 @@ public MenuType cr } @Override - public void openMenu(Player player, MenuProvider owner, ContainerData menu) { - player.openMenu(new WrappedMenuProvider(owner, menu)); + public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) { + player.openMenu(new WrappedMenuProvider(title, menu, data)); } @Override @@ -408,21 +408,22 @@ public T get() { } } - private record WrappedMenuProvider(MenuProvider owner, ContainerData menu) implements ExtendedScreenHandlerFactory { + private record WrappedMenuProvider(Component title, MenuConstructor menu, + ContainerData data) implements ExtendedScreenHandlerFactory { @Nullable @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { - return owner.createMenu(id, inventory, player); + return menu.createMenu(id, inventory, player); } @Override public Component getDisplayName() { - return owner.getDisplayName(); + return title; } @Override public void writeScreenOpeningData(ServerPlayer player, FriendlyByteBuf buf) { - menu.toBytes(buf); + data.toBytes(buf); } } diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 7e84f4ab3..c4c54ea20 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -28,6 +28,7 @@ import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceKey; @@ -41,6 +42,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.MenuConstructor; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; @@ -157,8 +159,8 @@ public MenuType cr } @Override - public void openMenu(Player player, MenuProvider owner, ContainerData menu) { - NetworkHooks.openScreen((ServerPlayer) player, owner, menu::toBytes); + public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) { + NetworkHooks.openScreen((ServerPlayer) player, new SimpleMenuProvider(menu, title), data::toBytes); } @Override From e7f56c4d25e611ebeb45c4e0f413fe5a6f58b1ba Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 26 Jan 2025 15:13:24 +0000 Subject: [PATCH 4/7] Remove ComponentMap I'd originally kept this around to prevent mods that use CC internals, but fa2140d00ba667902d361b4873804dd97dde3a19 has probably broken those anyway, so let's not worry too much. --- .../computercraft/shared/ModRegistry.java | 4 +- .../shared/computer/core/ComputerSystem.java | 8 +-- .../shared/computer/core/ServerComputer.java | 31 +++------ .../shared/util/ComponentMap.java | 66 ------------------- 4 files changed, 16 insertions(+), 93 deletions(-) delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/util/ComponentMap.java diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java index b12a8f0ca..7106355bf 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -28,6 +28,7 @@ import dan200.computercraft.shared.computer.blocks.ComputerBlock; import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity; import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import dan200.computercraft.shared.computer.items.CommandComputerItem; import dan200.computercraft.shared.computer.items.ComputerItem; @@ -89,7 +90,6 @@ import dan200.computercraft.shared.turtle.recipes.TurtleRecipe; import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe; import dan200.computercraft.shared.turtle.upgrades.*; -import dan200.computercraft.shared.util.ComponentMap; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.SingletonArgumentInfo; @@ -471,7 +471,7 @@ public static void register() { ComputerCraftAPI.registerAPIFactory(computer -> { var turtle = computer.getComponent(ComputerComponents.TURTLE); - var metrics = Objects.requireNonNull(computer.getComponent(ComponentMap.METRICS)); + var metrics = Objects.requireNonNull(computer.getComponent(ServerComputer.METRICS)); return turtle == null ? null : new TurtleAPI(metrics, (TurtleAccessInternal) turtle); }); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java index b69901513..7302fcccc 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java @@ -11,7 +11,6 @@ import dan200.computercraft.core.apis.ComputerAccess; import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.computer.ApiLifecycle; -import dan200.computercraft.shared.util.ComponentMap; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; @@ -26,11 +25,11 @@ final class ComputerSystem extends ComputerAccess implements IComputerSystem, ApiLifecycle { private final ServerComputer computer; private final IAPIEnvironment environment; - private final ComponentMap components; + private final Map, Object> components; private boolean active; - ComputerSystem(ServerComputer computer, IAPIEnvironment environment, ComponentMap components) { + ComputerSystem(ServerComputer computer, IAPIEnvironment environment, Map, Object> components) { super(environment); this.computer = computer; this.environment = environment; @@ -95,7 +94,8 @@ public IPeripheral getAvailablePeripheral(String name) { } @Override + @SuppressWarnings("unchecked") public @Nullable T getComponent(ComputerComponent component) { - return components.get(component); + return (T) components.get(component); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index ad32ee031..c246f3e56 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -25,18 +25,21 @@ import dan200.computercraft.shared.network.client.ClientNetworkContext; import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; import dan200.computercraft.shared.network.server.ServerNetworking; -import dan200.computercraft.shared.util.ComponentMap; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; public class ServerComputer implements ComputerEnvironment, ComputerEvents.Receiver { + public static final ComputerComponent METRICS = ComputerComponent.create("computercraft", "metrics"); + private final int instanceID; private final UUID instanceUUID = UUID.randomUUID(); @@ -52,14 +55,6 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei private int ticksSincePing; - @Deprecated - public ServerComputer( - ServerLevel level, BlockPos position, int computerID, @Nullable String label, ComputerFamily family, int terminalWidth, int terminalHeight, - ComponentMap baseComponents - ) { - this(level, position, properties(computerID, family).label(label).terminalSize(terminalWidth, terminalHeight).addComponents(baseComponents)); - } - public ServerComputer(ServerLevel level, BlockPos position, Properties properties) { this.level = level; this.position = position; @@ -70,14 +65,12 @@ public ServerComputer(ServerLevel level, BlockPos position, Properties propertie terminal = new NetworkedTerminal(properties.terminalWidth, properties.terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged); metrics = context.metrics().createMetricObserver(this); - var componentBuilder = ComponentMap.builder(); - componentBuilder.add(ComponentMap.METRICS, metrics); + properties.addComponent(METRICS, metrics); if (family == ComputerFamily.COMMAND) { - componentBuilder.add(ComputerComponents.ADMIN_COMPUTER, new AdminComputer() { + properties.addComponent(ComputerComponents.ADMIN_COMPUTER, new AdminComputer() { }); } - componentBuilder.add(properties.components.build()); - var components = componentBuilder.build(); + var components = Map.copyOf(properties.components); computer = new Computer(context.computerContext(), this, terminal, properties.computerID); computer.setLabel(properties.label); @@ -290,7 +283,7 @@ public static final class Properties { private int terminalWidth = Config.DEFAULT_COMPUTER_TERM_WIDTH; private int terminalHeight = Config.DEFAULT_COMPUTER_TERM_HEIGHT; - private final ComponentMap.Builder components = ComponentMap.builder(); + private final Map, Object> components = new HashMap<>(); private Properties(int computerID, ComputerFamily family) { this.computerID = computerID; @@ -310,12 +303,8 @@ public Properties terminalSize(int width, int height) { } public Properties addComponent(ComputerComponent component, T value) { - components.add(component, value); - return this; - } - - private Properties addComponents(ComponentMap components) { - this.components.add(components); + if (components.containsKey(component)) throw new IllegalArgumentException(component + " is already set"); + components.put(component, value); return this; } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentMap.java b/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentMap.java deleted file mode 100644 index cd89ba4a0..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentMap.java +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.util; - -import dan200.computercraft.api.component.ComputerComponent; -import dan200.computercraft.core.metrics.MetricsObserver; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -/** - * An immutable map of components. - */ -public final class ComponentMap { - public static final ComputerComponent METRICS = ComputerComponent.create("computercraft", "metrics"); - - private static final ComponentMap EMPTY = new ComponentMap(Map.of()); - - private final Map, Object> components; - - private ComponentMap(Map, Object> components) { - this.components = components; - } - - @SuppressWarnings("unchecked") - public @Nullable T get(ComputerComponent component) { - return (T) components.get(component); - } - - public static ComponentMap empty() { - return EMPTY; - } - - public static Builder builder() { - return new Builder(); - } - - public static final class Builder { - private final Map, Object> components = new HashMap<>(); - - private Builder() { - } - - public Builder add(ComputerComponent component, T value) { - addImpl(component, value); - return this; - } - - public Builder add(ComponentMap components) { - for (var component : components.components.entrySet()) addImpl(component.getKey(), component.getValue()); - return this; - } - - private void addImpl(ComputerComponent component, Object value) { - if (components.containsKey(component)) throw new IllegalArgumentException(component + " is already set"); - components.put(component, value); - } - - public ComponentMap build() { - return new ComponentMap(Map.copyOf(components)); - } - } -} From 7d8f609c49244bbabb5a1c65d56a66fbbcc05f14 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 26 Jan 2025 22:27:50 +0000 Subject: [PATCH 5/7] literally sobbing rn --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 138d85736..1a25729b7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -30,3 +30,5 @@ body: Description of the bug. Please include the following: - Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this! - Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed. + + ![A gif of burning text reading "Upload your logs!!!"](https://tweaked.cc/images/logs.gif) From b69a44a927fd7563af701808712bc5ebe3ba5b73 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 27 Jan 2025 22:43:29 +0000 Subject: [PATCH 6/7] Redo turtle move checks Oh, this is so broken, and really has been since the 1.13 update, if not earlier. - Fix call to isUnobstructed using the bounding box of the *destination* block rather than the turtle. This is almost always air, so the box is empty. - Because the above check has been wrong for so many years, we now significantly relax the "can push" checks for entities. We now allow pushing entities in any direction. We also remove the "isUnobstructed" check for the destination entity pos. This causes problems (if two entities are standing on a turtle, they'll obstruct each other), and given pistons don't perform such a check, I don't think we need it. - Also do a bit of cleanup around air/liquid checks. We often ended up reading the block state multiple times, which is a little ugly. --- .../shared/computer/core/ServerComputer.java | 1 - .../monitor/MonitorBlockEntity.java | 3 +- .../shared/turtle/core/TurtleBrain.java | 5 +- .../turtle/core/TurtleDetectCommand.java | 7 +- .../shared/turtle/core/TurtleMoveCommand.java | 74 ++++----- .../computercraft/shared/util/WorldUtil.java | 9 +- .../computercraft/gametest/Turtle_Test.kt | 21 +++ .../gametest/core/ManagedComputers.kt | 4 + .../computercraft/gametest/core/TestHooks.kt | 2 + .../turtle_test.move_push_entity.snbt | 140 ++++++++++++++++++ .../shared/platform/PlatformHelperImpl.java | 5 +- 11 files changed, 215 insertions(+), 56 deletions(-) create mode 100644 projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index c246f3e56..5582c2f58 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -276,7 +276,6 @@ public static Properties properties(int computerID, ComputerFamily family) { } public static final class Properties { - private final int computerID; private @Nullable String label; private final ComputerFamily family; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java index 936a753cd..ae4a434b0 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorBlockEntity.java @@ -18,6 +18,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +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; @@ -237,7 +238,7 @@ private void updateBlockState() { getLevel().setBlock(getBlockPos(), getBlockState() .setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( yIndex < height - 1, yIndex > 0, - xIndex > 0, xIndex < width - 1)), 2); + xIndex > 0, xIndex < width - 1)), Block.UPDATE_CLIENTS); } // region Sizing and placement stuff diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index 87090e614..4c62cb236 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -36,6 +36,7 @@ import net.minecraft.world.entity.MoverType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -280,7 +281,7 @@ public boolean teleportTo(Level world, BlockPos pos) { try { // We use Block.UPDATE_CLIENTS here to ensure that neighbour updates caused in Block.updateNeighbourShapes // are sent to the client. We want to avoid doing a full block update until the turtle state is copied over. - if (world.setBlock(pos, newState, 2)) { + if (world.setBlock(pos, newState, Block.UPDATE_CLIENTS)) { var block = world.getBlockState(pos).getBlock(); if (block == oldBlock.getBlock()) { var newTile = world.getBlockEntity(pos); @@ -691,7 +692,7 @@ private void updateAnimation() { } var aabb = new AABB(minX, minY, minZ, maxX, maxY, maxZ); - var list = world.getEntitiesOfClass(Entity.class, aabb, TurtleBrain::canPush); + var list = world.getEntities((Entity) null, aabb, TurtleBrain::canPush); if (!list.isEmpty()) { double pushStep = 1.0f / ANIM_DURATION; var pushStepX = moveDir.getStepX() * pushStep; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java index 480ad209e..2ce7fb27c 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java @@ -22,11 +22,10 @@ public TurtleCommandResult execute(ITurtleAccess turtle) { var direction = this.direction.toWorldDir(turtle); // Check if thing in front is air or not - var world = turtle.getLevel(); - var oldPosition = turtle.getPosition(); - var newPosition = oldPosition.relative(direction); + var level = turtle.getLevel(); + var pos = turtle.getPosition().relative(direction); - return !WorldUtil.isLiquidBlock(world, newPosition) && !world.isEmptyBlock(newPosition) + return !WorldUtil.isEmptyBlock(level.getBlockState(pos)) ? TurtleCommandResult.success() : TurtleCommandResult.failure(); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java index 02778bb93..c6990fa4b 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java @@ -13,9 +13,9 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; public class TurtleMoveCommand implements TurtleCommand { private final MoveDirection direction; @@ -30,57 +30,32 @@ public TurtleCommandResult execute(ITurtleAccess turtle) { var direction = this.direction.toWorldDir(turtle); // Check if we can move - var oldWorld = (ServerLevel) turtle.getLevel(); + var level = (ServerLevel) turtle.getLevel(); var oldPosition = turtle.getPosition(); var newPosition = oldPosition.relative(direction); var turtlePlayer = TurtlePlayer.getWithPosition(turtle, oldPosition, direction); - var canEnterResult = canEnter(turtlePlayer, oldWorld, newPosition); - if (!canEnterResult.isSuccess()) { - return canEnterResult; - } + var canEnterResult = canEnter(turtlePlayer, level, newPosition); + if (!canEnterResult.isSuccess()) return canEnterResult; - // Check existing block is air or replaceable - var state = oldWorld.getBlockState(newPosition); - if (!oldWorld.isEmptyBlock(newPosition) && - !WorldUtil.isLiquidBlock(oldWorld, newPosition) && - !state.canBeReplaced()) { + // Check existing block is air or replaceable. + var existingState = level.getBlockState(newPosition); + if (!(WorldUtil.isEmptyBlock(existingState) || existingState.canBeReplaced())) { return TurtleCommandResult.failure("Movement obstructed"); } - // Check there isn't anything in the way - var collision = state.getCollisionShape(oldWorld, oldPosition).move( - newPosition.getX(), - newPosition.getY(), - newPosition.getZ() - ); - - if (!oldWorld.isUnobstructed(null, collision)) { - if (!Config.turtlesCanPush || this.direction == MoveDirection.UP || this.direction == MoveDirection.DOWN) { - return TurtleCommandResult.failure("Movement obstructed"); - } - - // Check there is space for all the pushable entities to be pushed - var list = oldWorld.getEntitiesOfClass(Entity.class, getBox(collision), x -> x != null && x.isAlive() && x.blocksBuilding); - for (var entity : list) { - var pushedBB = entity.getBoundingBox().move( - direction.getStepX(), - direction.getStepY(), - direction.getStepZ() - ); - if (!oldWorld.isUnobstructed(null, Shapes.create(pushedBB))) { - return TurtleCommandResult.failure("Movement obstructed"); - } - } + // Check there isn't an entity in the way. + var turtleShape = level.getBlockState(oldPosition).getCollisionShape(level, oldPosition) + .move(newPosition.getX(), newPosition.getY(), newPosition.getZ()); + if (!level.isUnobstructed(null, turtleShape) && !canPushEntities(level, turtleShape.bounds())) { + return TurtleCommandResult.failure("Movement obstructed"); } // Check fuel level - if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) { - return TurtleCommandResult.failure("Out of fuel"); - } + if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) return TurtleCommandResult.failure("Out of fuel"); // Move - if (!turtle.teleportTo(oldWorld, newPosition)) return TurtleCommandResult.failure("Movement failed"); + if (!turtle.teleportTo(level, newPosition)) return TurtleCommandResult.failure("Movement failed"); // Consume fuel turtle.consumeFuel(1); @@ -114,9 +89,20 @@ private static TurtleCommandResult canEnter(TurtlePlayer turtlePlayer, ServerLev return TurtleCommandResult.success(); } - private static AABB getBox(VoxelShape shape) { - return shape.isEmpty() ? EMPTY_BOX : shape.bounds(); - } - private static final AABB EMPTY_BOX = new AABB(0, 0, 0, 0, 0, 0); + /** + * Determine if all entities in the given bounds can be pushed by the turtle. + * + * @param level The current level. + * @param bounds The bounding box. + * @return Whether all entities can be pushed. + */ + private boolean canPushEntities(Level level, AABB bounds) { + if (!Config.turtlesCanPush) return false; + + // Check there is space for all the pushable entities to be pushed + return level.getEntities((Entity) null, bounds, e -> e.isAlive() + && !e.isSpectator() && e.blocksBuilding && e.getPistonPushReaction() == PushReaction.IGNORE + ).isEmpty(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java index 049e4bf37..046967211 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/WorldUtil.java @@ -29,9 +29,14 @@ import javax.annotation.Nullable; public final class WorldUtil { + @SuppressWarnings("deprecation") public static boolean isLiquidBlock(Level world, BlockPos pos) { - if (!world.isInWorldBounds(pos)) return false; - return world.getBlockState(pos).liquid(); + return world.isInWorldBounds(pos) && world.getBlockState(pos).liquid(); + } + + @SuppressWarnings("deprecation") + public static boolean isEmptyBlock(BlockState state) { + return state.isAir() || state.liquid(); } public static boolean isVecInside(VoxelShape shape, Vec3 vec) { diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt index 69b113f0b..5310f52cd 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt @@ -642,6 +642,27 @@ class Turtle_Test { } } + /** + * Test turtles can push entities. + */ + @GameTest + fun Move_push_entity(helper: GameTestHelper) = helper.sequence { + thenOnComputer { turtle.up().await().assertArrayEquals(true) } + thenIdle(9) + thenExecute { + // The turtle has moved up + helper.assertBlockPresent(ModRegistry.Blocks.TURTLE_NORMAL.get(), BlockPos(2, 3, 2)) + + // As has the villager + val pos = BlockPos(2, 4, 2) + helper.assertEntityPresent(EntityType.VILLAGER, pos) + + val villager = helper.getEntity(EntityType.VILLAGER) + val expectedY = helper.absolutePos(pos).y - 0.125 + if (villager.y < expectedY) helper.fail("Expected villager at y>=$expectedY, but at ${villager.y}", pos) + } + } + /** * Test a turtle can attack an entity and capture its drops. */ diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt index 05a06ee4f..6804668b3 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt @@ -36,6 +36,10 @@ object ManagedComputers : ILuaMachine.Factory { private val LOGGER = LoggerFactory.getLogger(ManagedComputers::class.java) private val computers: MutableMap Unit>> = mutableMapOf() + internal fun reset() { + computers.clear() + } + internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor { val monitor = Monitor(test, label) computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add { diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt index b1510719e..d18cda24a 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/TestHooks.kt @@ -73,6 +73,8 @@ object TestHooks { LOG.info("Cleaning up after last run") GameTestRunner.clearAllTests(server.overworld(), BlockPos(0, -60, 0), GameTestTicker.SINGLETON, 200) + ManagedComputers.reset() + // Delete server context and add one with a mutable machine factory. This allows us to set the factory for // specific test batches without having to reset all computers. for (computer in ServerContext.get(server).registry().computers) { diff --git a/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt new file mode 100644 index 000000000..2c043fe6d --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.move_push_entity.snbt @@ -0,0 +1,140 @@ +{ + 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:white_stained_glass"}, + {pos: [1, 1, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 1, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 1, Label: "turtle_test.move_push_entity", Fuel: 80, Items: [], On: 1b, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 1, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 1, 3], state: "minecraft:white_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:white_stained_glass"}, + {pos: [1, 2, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 2, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 2, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 2, 3], state: "minecraft:white_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:white_stained_glass"}, + {pos: [1, 3, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 3, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 3, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 3, 3], state: "minecraft:white_stained_glass"}, + {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:white_stained_glass"}, + {pos: [1, 4, 2], state: "minecraft:white_stained_glass"}, + {pos: [1, 4, 3], state: "minecraft:white_stained_glass"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:white_stained_glass"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:white_stained_glass"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:white_stained_glass"}, + {pos: [3, 4, 2], state: "minecraft:white_stained_glass"}, + {pos: [3, 4, 3], state: "minecraft:white_stained_glass"}, + {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: [ + {blockPos: [2, 1, 2], pos: [2.5d, 1.875d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Age: 0, Air: 300s, ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.5d, Name: "minecraft:generic.movement_speed"}, {Base: 48.0d, Modifiers: [{Amount: -0.01165046535152748d, Name: "Random spawn bonus", Operation: 1, UUID: [I; 1412502412, 1522745411, -1211155694, 2103054347]}], Name: "minecraft:generic.follow_range"}], Brain: {memories: {}}, CanPickUpLoot: 1b, DeathTime: 0s, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, FoodLevel: 0b, ForcedAge: 0, Gossips: [], HandDropChances: [0.085f, 0.085f], HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Inventory: [], Invulnerable: 0b, LastGossipDecay: 52357L, LastRestock: 0L, LeftHanded: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], OnGround: 1b, PersistenceRequired: 0b, PortalCooldown: 0, Pos: [-33.5d, 58.875d, -21.5d], RestocksToday: 0, Rotation: [-102.704926f, 0.0f], UUID: [I; 164071932, -867285780, -1817215456, -2129864016], VillagerData: {level: 1, profession: "minecraft:none", type: "minecraft:desert"}, Xp: 0, id: "minecraft:villager"}} + ], + palette: [ + "minecraft:polished_andesite", + "minecraft:white_stained_glass", + "minecraft:air", + "computercraft:turtle_normal{facing:west,waterlogged:false}" + ] +} diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java index 11690ae41..700f8a777 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/platform/PlatformHelperImpl.java @@ -408,8 +408,9 @@ public T get() { } } - private record WrappedMenuProvider(Component title, MenuConstructor menu, - ContainerData data) implements ExtendedScreenHandlerFactory { + private record WrappedMenuProvider( + Component title, MenuConstructor menu, ContainerData data + ) implements ExtendedScreenHandlerFactory { @Nullable @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { From 43604858807e496132386e385b71fef5ce6e8ccf Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 31 Jan 2025 21:09:57 +0000 Subject: [PATCH 7/7] Bump CC:T to 1.114.4 --- gradle.properties | 2 +- .../data/computercraft/lua/rom/help/changelog.md | 9 +++++++++ .../data/computercraft/lua/rom/help/whatsnew.md | 10 +++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9f56b970c..4f9e52b8b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error # Mod properties isUnstable=false -modVersion=1.114.3 +modVersion=1.114.4 # Minecraft properties: We want to configure this here so we can read it in settings.gradle mcVersion=1.20.1 diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md index 892c96170..19e4f28f4 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,3 +1,12 @@ +# New features in CC: Tweaked 1.114.4 + +* Allow typing/pasting any character in the CC charset. + +Several bug fixes: +* Fix command computers being exposed as peripherals (Forge only). +* Fix command computers having NBT set when placed in a Create contraption. +* Use correct bounding box when checking for entities in turtle movement. + # New features in CC: Tweaked 1.114.3 * `wget` now prints the error that occurred, rather than a generic "Failed" (tizu69). diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index a8fc8a0e6..d1929e651 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,10 +1,10 @@ -New features in CC: Tweaked 1.114.3 +New features in CC: Tweaked 1.114.4 -* `wget` now prints the error that occurred, rather than a generic "Failed" (tizu69). -* Update several translations. +* Allow typing/pasting any character in the CC charset. Several bug fixes: -* Fix `fs.isDriveRoot` returning true for non-existent files. -* Fix possible memory leak when sending terminal contents. +* Fix command computers being exposed as peripherals (Forge only). +* Fix command computers having NBT set when placed in a Create contraption. +* Use correct bounding box when checking for entities in turtle movement. Type "help changelog" to see the full version history.