diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java index ddc566f17..fa84f3c86 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java +++ b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java @@ -24,7 +24,6 @@ import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu; -import dan200.computercraft.shared.computer.inventory.ViewComputerMenu; import dan200.computercraft.shared.media.items.DiskItem; import net.minecraft.Util; import net.minecraft.client.Minecraft; @@ -101,15 +100,12 @@ public final class ClientRegistry { public static void registerMenuScreens(RegisterMenuScreen register) { register.>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new); - register.>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new); register.>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new); register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new); register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new); register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new); register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new); - - register.>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new); } public interface RegisterMenuScreen { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java similarity index 98% rename from projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java rename to projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java index 7e0fc4f5a..8f5a4b555 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/JEIComputerCraft.java @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MPL-2.0 -package dan200.computercraft.shared.integration.jei; +package dan200.computercraft.client.integration.jei; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.TurtleSide; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java similarity index 98% rename from projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java rename to projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java index 9e9246753..d3bd54036 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/jei/RecipeResolver.java @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MPL-2.0 -package dan200.computercraft.shared.integration.jei; +package dan200.computercraft.client.integration.jei; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.integration.RecipeModHelpers; 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 0c7fd6df1..527f704ce 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -27,12 +27,10 @@ import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider; import dan200.computercraft.shared.common.HeldItemMenu; import dan200.computercraft.shared.computer.blocks.CommandComputerBlock; -import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity; 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.inventory.ComputerMenuWithoutInventory; -import dan200.computercraft.shared.computer.inventory.ViewComputerMenu; import dan200.computercraft.shared.computer.items.AbstractComputerItem; import dan200.computercraft.shared.computer.items.CommandComputerItem; import dan200.computercraft.shared.computer.items.ComputerItem; @@ -155,8 +153,7 @@ public final class ModRegistry { () -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL)); public static final RegistryEntry> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced", () -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED)); - - public static final RegistryEntry> COMPUTER_COMMAND = REGISTRY.register("computer_command", + public static final RegistryEntry> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND)); public static final RegistryEntry TURTLE_NORMAL = REGISTRY.register("turtle_normal", @@ -199,8 +196,8 @@ public final class ModRegistry { ofBlock(Blocks.COMPUTER_NORMAL, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_NORMAL.get(), p, s, ComputerFamily.NORMAL)); public static final RegistryEntry> COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_ADVANCED.get(), p, s, ComputerFamily.ADVANCED)); - public static final RegistryEntry> COMPUTER_COMMAND = - ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new CommandComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s)); + public static final RegistryEntry> COMPUTER_COMMAND = + ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s, ComputerFamily.COMMAND)); public static final RegistryEntry> TURTLE_NORMAL = ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL)); @@ -413,9 +410,6 @@ public final class ModRegistry { public static final RegistryEntry> COMPUTER = REGISTRY.register("computer", () -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data))); - public static final RegistryEntry> POCKET_COMPUTER = REGISTRY.register("pocket_computer", - () -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER.get(), id, inv, data))); - public static final RegistryEntry> POCKET_COMPUTER_NO_TERM = REGISTRY.register("pocket_computer_no_term", () -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER_NO_TERM.get(), id, inv, data))); @@ -433,9 +427,6 @@ public final class ModRegistry { HeldItemContainerData.STREAM_CODEC, (id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.hand()) )); - - public static final RegistryEntry> VIEW_COMPUTER = REGISTRY.register("view_computer", - () -> ContainerData.toType(ComputerContainerData.STREAM_CODEC, ViewComputerMenu::new)); } static class ArgumentTypes { 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 fc2fbf4b6..5311b6063 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 @@ -18,7 +18,7 @@ import dan200.computercraft.shared.command.text.TableBuilder; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerContext; -import dan200.computercraft.shared.computer.inventory.ViewComputerMenu; +import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import dan200.computercraft.shared.computer.metrics.basic.Aggregate; import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric; import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver; @@ -268,7 +268,7 @@ public final class CommandComputerCraft { @Override public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) { - return new ViewComputerMenu(id, player, computer); + return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer); } }); return 1; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index faf8acd99..d29389733 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -10,15 +10,19 @@ import dan200.computercraft.api.detail.BlockReference; import dan200.computercraft.api.detail.VanillaDetailRegistries; import dan200.computercraft.api.lua.*; import dan200.computercraft.core.Logging; -import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity; +import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.util.NBTUtil; +import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,9 +35,10 @@ import java.util.*; public class CommandAPI implements ILuaAPI { private static final Logger LOG = LoggerFactory.getLogger(CommandAPI.class); - private final CommandComputerBlockEntity computer; + private final ServerComputer computer; + private final OutputReceiver receiver = new OutputReceiver(); - public CommandAPI(CommandComputerBlockEntity computer) { + public CommandAPI(ServerComputer computer) { this.computer = computer; } @@ -48,16 +53,15 @@ public class CommandAPI implements ILuaAPI { private Object[] doCommand(String command) { var server = computer.getLevel().getServer(); - if (server == null || !server.isCommandBlockEnabled()) { + if (!server.isCommandBlockEnabled()) { return new Object[]{ false, createOutput("Command blocks disabled by server") }; } var commandManager = server.getCommands(); - var receiver = computer.getReceiver(); try { receiver.clearOutput(); var state = new CommandState(); - var source = computer.getSource().withCallback((success, x) -> { + var source = getSource().withCallback((success, x) -> { if (success) state.successes++; }); commandManager.performPrefixedCommand(source, command); @@ -142,7 +146,6 @@ public class CommandAPI implements ILuaAPI { public final List list(IArguments args) throws LuaException { var server = computer.getLevel().getServer(); - if (server == null) return List.of(); CommandNode node = server.getCommands().getDispatcher().getRoot(); for (var j = 0; j < args.count(); j++) { var name = args.getString(j); @@ -169,7 +172,7 @@ public class CommandAPI implements ILuaAPI { @LuaFunction public final Object[] getBlockPosition() { // This is probably safe to do on the Lua thread. Probably. - var pos = computer.getBlockPos(); + var pos = computer.getPosition(); return new Object[]{ pos.getX(), pos.getY(), pos.getZ() }; } @@ -194,7 +197,6 @@ public class CommandAPI implements ILuaAPI { * @throws LuaException If trying to get information about more than 4096 blocks. * @cc.since 1.76 * @cc.changed 1.99 Added {@code dimension} argument. - * * @cc.usage Print out all blocks in a cube around the computer. * *
{@code
@@ -228,7 +230,7 @@ public class CommandAPI implements ILuaAPI {
             Math.max(minY, maxY),
             Math.max(minZ, maxZ)
         );
-        if (world == null || !world.isInWorldBounds(min) || !world.isInWorldBounds(max)) {
+        if (!world.isInWorldBounds(min) || !world.isInWorldBounds(max)) {
             throw new LuaException("Co-ordinates out of range");
         }
 
@@ -273,10 +275,9 @@ public class CommandAPI implements ILuaAPI {
     }
 
     private Level getLevel(Optional id) throws LuaException {
-        var currentLevel = (ServerLevel) computer.getLevel();
-        if (currentLevel == null) throw new LuaException("No world exists");
+        var currentLevel = computer.getLevel();
 
-        if (!id.isPresent()) return currentLevel;
+        if (id.isEmpty()) return currentLevel;
 
         var dimensionId = ResourceLocation.tryParse(id.get());
         if (dimensionId == null) throw new LuaException("Invalid dimension name");
@@ -286,4 +287,52 @@ public class CommandAPI implements ILuaAPI {
 
         return level;
     }
+
+    private CommandSourceStack getSource() {
+        var name = "@";
+        var label = computer.getLabel();
+        if (label != null) name = label;
+
+        return new CommandSourceStack(receiver,
+            Vec3.atCenterOf(computer.getPosition()), Vec2.ZERO,
+            computer.getLevel(), 2,
+            name, Component.literal(name),
+            computer.getLevel().getServer(), null
+        );
+    }
+
+    /**
+     * A {@link CommandSource} that consumes output messages and stores them to a list.
+     */
+    private final class OutputReceiver implements CommandSource {
+        private final List output = new ArrayList<>();
+
+        void clearOutput() {
+            output.clear();
+        }
+
+        List copyOutput() {
+            return List.copyOf(output);
+        }
+
+        @Override
+        public void sendSystemMessage(Component textComponent) {
+            output.add(textComponent.getString());
+        }
+
+        @Override
+        public boolean acceptsSuccess() {
+            return true;
+        }
+
+        @Override
+        public boolean acceptsFailure() {
+            return true;
+        }
+
+        @Override
+        public boolean shouldInformAdmins() {
+            return computer.getLevel().getGameRules().getBoolean(GameRules.RULE_COMMANDBLOCKOUTPUT);
+        }
+    }
 }
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 5716ffcc8..ea23cc0e8 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
@@ -82,7 +82,8 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
     }
 
     public boolean isUsable(Player player) {
-        return BaseContainerBlockEntity.canUnlock(player, lockCode, getDisplayName())
+        return getFamily().checkUsable(player)
+            && BaseContainerBlockEntity.canUnlock(player, lockCode, getDisplayName())
             && Container.stillValidBlockEntity(this, player, getInteractRange());
     }
 
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlock.java
index f6d8ba415..fbbaea72e 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlock.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlock.java
@@ -18,7 +18,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
  * @param  The type of the computer block entity.
  * @see dan200.computercraft.shared.computer.items.CommandComputerItem
  */
-public class CommandComputerBlock extends ComputerBlock implements GameMasterBlock {
+public class CommandComputerBlock extends ComputerBlock implements GameMasterBlock {
     private static final MapCodec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
         BlockCodecs.propertiesCodec(),
         BlockCodecs.blockEntityCodec(x -> x.type)
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlockEntity.java
deleted file mode 100644
index 1f831574f..000000000
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlockEntity.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
-//
-// SPDX-License-Identifier: LicenseRef-CCPL
-
-package dan200.computercraft.shared.computer.blocks;
-
-import dan200.computercraft.shared.computer.apis.CommandAPI;
-import dan200.computercraft.shared.computer.core.ComputerFamily;
-import dan200.computercraft.shared.computer.core.ServerComputer;
-import dan200.computercraft.shared.config.Config;
-import net.minecraft.commands.CommandSource;
-import net.minecraft.commands.CommandSourceStack;
-import net.minecraft.core.BlockPos;
-import net.minecraft.network.chat.Component;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.level.GameRules;
-import net.minecraft.world.level.block.entity.BlockEntityType;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.phys.Vec2;
-import net.minecraft.world.phys.Vec3;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CommandComputerBlockEntity extends ComputerBlockEntity {
-    public class CommandReceiver implements CommandSource {
-        private final List output = new ArrayList<>();
-
-        public void clearOutput() {
-            output.clear();
-        }
-
-        public List copyOutput() {
-            return new ArrayList<>(output);
-        }
-
-        @Override
-        public void sendSystemMessage(Component textComponent) {
-            output.add(textComponent.getString());
-        }
-
-        @Override
-        public boolean acceptsSuccess() {
-            return true;
-        }
-
-        @Override
-        public boolean acceptsFailure() {
-            return true;
-        }
-
-        @Override
-        public boolean shouldInformAdmins() {
-            return getLevel().getGameRules().getBoolean(GameRules.RULE_COMMANDBLOCKOUTPUT);
-        }
-    }
-
-    private final CommandReceiver receiver;
-
-    public CommandComputerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) {
-        super(type, pos, state, ComputerFamily.COMMAND);
-        receiver = new CommandReceiver();
-    }
-
-    public CommandReceiver getReceiver() {
-        return receiver;
-    }
-
-    public CommandSourceStack getSource() {
-        var computer = getServerComputer();
-        var name = "@";
-        if (computer != null) {
-            var label = computer.getLabel();
-            if (label != null) name = label;
-        }
-
-        return new CommandSourceStack(receiver,
-            Vec3.atCenterOf(worldPosition), Vec2.ZERO,
-            (ServerLevel) getLevel(), 2,
-            name, Component.literal(name),
-            getLevel().getServer(), null
-        );
-    }
-
-    @Override
-    protected ServerComputer createComputer(int id) {
-        var computer = super.createComputer(id);
-        computer.addAPI(new CommandAPI(this));
-        return computer;
-    }
-
-    @Override
-    public boolean isUsable(Player player) {
-        return isCommandUsable(player) && super.isUsable(player);
-    }
-
-    public static boolean isCommandUsable(Player player) {
-        var server = player.getServer();
-        if (server == null || !server.isCommandBlockEnabled()) {
-            player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
-            return false;
-        } else if (!canUseCommandBlock(player)) {
-            player.displayClientMessage(Component.translatable("advMode.notAllowed"), true);
-            return false;
-        }
-
-        return true;
-    }
-
-    private static boolean canUseCommandBlock(Player player) {
-        return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2);
-    }
-}
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlockEntity.java
index 89da54571..db508ab71 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlockEntity.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerBlockEntity.java
@@ -67,7 +67,7 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
     @Nullable
     @Override
     public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
-        return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily());
+        return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, inventory, this::isUsableByPlayer, createServerComputer());
     }
 
     public IPeripheral peripheral() {
diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java
index 491fb0a5f..099c74e72 100644
--- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java
+++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java
@@ -4,8 +4,45 @@
 
 package dan200.computercraft.shared.computer.core;
 
+import dan200.computercraft.shared.config.Config;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.entity.player.Player;
+
 public enum ComputerFamily {
     NORMAL,
     ADVANCED,
-    COMMAND,
+    COMMAND;
+
+    /**
+     * Check whether computers with this family can be used by the provided player.
+     * 

+ * This method is not pure. On failure, the method may send a message to the player telling them why they cannot + * interact with the computer. + * + * @param player The player trying to use a computer. + * @return Whether this computer family can be used. + */ + public boolean checkUsable(Player player) { + return switch (this) { + case NORMAL, ADVANCED -> true; + case COMMAND -> checkCommandUsable(player); + }; + } + + private static boolean checkCommandUsable(Player player) { + var server = player.getServer(); + if (server == null || !server.isCommandBlockEnabled()) { + player.displayClientMessage(Component.translatable("advMode.notEnabled"), true); + return false; + } else if (!canUseCommandBlock(player)) { + player.displayClientMessage(Component.translatable("advMode.notAllowed"), true); + return false; + } + + return true; + } + + private static boolean canUseCommandBlock(Player player) { + return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2); + } } 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 1d316a69d..266f2157f 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 @@ -8,11 +8,12 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.filesystem.WritableMount; import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.core.apis.IAPIEnvironment; +import dan200.computercraft.api.peripheral.WorkMonitor; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ComputerEnvironment; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.metrics.MetricsObserver; +import dan200.computercraft.shared.computer.apis.CommandAPI; import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.computer.terminal.TerminalState; @@ -23,6 +24,7 @@ import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; import dan200.computercraft.shared.network.server.ServerNetworking; 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; @@ -58,6 +60,8 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { computer = new Computer(context.computerContext(), this, terminal, computerID); computer.setLabel(label); + + if (family == ComputerFamily.COMMAND) addAPI(new CommandAPI(this)); } public ComputerFamily getFamily() { @@ -68,32 +72,20 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { return level; } - public void setLevel(ServerLevel level) { - this.level = level; - } - public BlockPos getPosition() { return position; } - public void setPosition(BlockPos pos) { - position = new BlockPos(pos); - } - - public IAPIEnvironment getAPIEnvironment() { - return computer.getAPIEnvironment(); - } - - public Computer getComputer() { - return computer; + public void setPosition(ServerLevel level, BlockPos pos) { + this.level = level; + position = pos.immutable(); } protected void markTerminalChanged() { terminalChanged.set(true); } - - public void tickServer() { + protected void tickServer() { ticksSincePing++; computer.tick(); if (terminalChanged.getAndSet(false)) onTerminalChanged(); @@ -111,10 +103,15 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { ticksSincePing = 0; } - public boolean hasTimedOut() { + boolean hasTimedOut() { return ticksSincePing > 100; } + /** + * Get a bitmask returning which sides on the computer have changed, resetting the internal state. + * + * @return What sides on the computer have changed. + */ public int pollAndResetChanges() { return computer.pollAndResetChanges(); } @@ -133,6 +130,17 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { ServerContext.get(level.getServer()).registry().remove(this); } + /** + * Check whether this computer is usable by a player. + * + * @param player The player trying to use this computer. + * @return Whether this computer can be used. + */ + public final boolean checkUsable(Player player) { + return ServerContext.get(level.getServer()).registry().get(instanceUUID) == this + && getFamily().checkUsable(player); + } + private void sendToAllInteracting(Function> createPacket) { var server = level.getServer(); @@ -169,25 +177,21 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { @Override public void turnOn() { - // Turn on computer.turnOn(); } @Override public void shutdown() { - // Shutdown computer.shutdown(); } @Override public void reboot() { - // Reboot computer.reboot(); } @Override public void queueEvent(String event, @Nullable Object[] arguments) { - // Queue event computer.queueEvent(event, arguments); } @@ -239,6 +243,10 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { return metrics; } + public WorkMonitor getMainThreadMonitor() { + return computer.getMainThreadMonitor(); + } + @Override public @Nullable WritableMount createRootMount() { return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + computer.getID(), Config.computerSpaceLimit); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java index 443383e51..5f8197dce 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/AbstractComputerMenu.java @@ -59,7 +59,7 @@ public abstract class AbstractComputerMenu extends AbstractContainerMenu impleme @Override public boolean stillValid(Player player) { - return canUse.test(player); + return (computer == null || computer.checkUsable(player)) && canUse.test(player); } public ComputerFamily getFamily() { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java index c3bf43dfd..971b6a37d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java @@ -4,7 +4,6 @@ package dan200.computercraft.shared.computer.inventory; -import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.container.InvisibleSlot; import dan200.computercraft.shared.network.container.ComputerContainerData; @@ -23,9 +22,9 @@ import java.util.function.Predicate; public class ComputerMenuWithoutInventory extends AbstractComputerMenu { public ComputerMenuWithoutInventory( MenuType type, int id, Inventory player, Predicate canUse, - ServerComputer computer, ComputerFamily family + ServerComputer computer ) { - super(type, id, canUse, family, computer, null); + super(type, id, canUse, computer.getFamily(), computer, null); addSlots(player); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ViewComputerMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ViewComputerMenu.java deleted file mode 100644 index 2dbb56262..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/inventory/ViewComputerMenu.java +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.computer.inventory; - -import dan200.computercraft.shared.ModRegistry; -import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity; -import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.computer.core.ServerContext; -import dan200.computercraft.shared.network.container.ComputerContainerData; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; - - -public class ViewComputerMenu extends ComputerMenuWithoutInventory { - public ViewComputerMenu(int id, Inventory player, ServerComputer computer) { - super(ModRegistry.Menus.VIEW_COMPUTER.get(), id, player, p -> canInteractWith(computer, p), computer, computer.getFamily()); - } - - public ViewComputerMenu(int id, Inventory player, ComputerContainerData data) { - super(ModRegistry.Menus.VIEW_COMPUTER.get(), id, player, data); - } - - private static boolean canInteractWith(ServerComputer computer, Player player) { - // If this computer no longer exists then discard it. - if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceUUID()) != computer) { - return false; - } - - // If we're a command computer then ensure we're in creative - if (computer.getFamily() == ComputerFamily.COMMAND && !CommandComputerBlockEntity.isCommandUsable(player)) { - return false; - } - - return true; - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index e6bf06627..ab67d5bc8 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -138,10 +138,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces } public synchronized void updateValues(@Nullable Entity entity, ItemStack stack, @Nullable IPocketUpgrade upgrade) { - if (entity != null) { - setLevel((ServerLevel) entity.getCommandSenderWorld()); - setPosition(entity.blockPosition()); - } + if (entity != null) setPosition((ServerLevel) entity.level(), entity.blockPosition()); // If a new entity has picked it up then rebroadcast the terminal to them if (entity != this.entity && entity instanceof ServerPlayer) markTerminalChanged(); @@ -156,7 +153,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces } @Override - public void tickServer() { + protected void tickServer() { super.tickServer(); // Find any players which have gone missing and remove them from the tracking list. 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 index 7cd84af52..5c091afb1 100644 --- 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 @@ -45,12 +45,12 @@ public class PocketComputerMenuProvider implements MenuProvider { @Override public AbstractContainerMenu createMenu(int id, Inventory inventory, Player entity) { return new ComputerMenuWithoutInventory( - isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.POCKET_COMPUTER.get(), id, inventory, + 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, item.getFamily() + 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 99f5c93fd..31d2dd521 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 @@ -53,10 +53,9 @@ public class PocketComputerItem extends Item implements IMedia { this.family = family; } - private boolean tick(ItemStack stack, Level world, Entity entity, PocketServerComputer computer) { + private boolean tick(ItemStack stack, Entity entity, PocketServerComputer computer) { var upgrade = getUpgrade(stack); - computer.setLevel((ServerLevel) world); computer.updateValues(entity, stack, upgrade); var changed = false; @@ -87,7 +86,7 @@ public class PocketComputerItem extends Item implements IMedia { var computer = createServerComputer((ServerLevel) world, entity, inventory, stack); computer.keepAlive(); - var changed = tick(stack, world, entity, computer); + var changed = tick(stack, entity, computer); if (changed && inventory != null) inventory.setChanged(); } @@ -97,7 +96,7 @@ public class PocketComputerItem extends Item implements IMedia { if (level.isClientSide || level.getServer() == null) return false; var computer = getServerComputer(level.getServer(), stack); - if (computer != null && tick(stack, entity.level(), entity, computer)) entity.setItem(stack.copy()); + if (computer != null && tick(stack, entity, computer)) entity.setItem(stack.copy()); return false; } @@ -175,7 +174,7 @@ public class PocketComputerItem extends Item implements IMedia { if (inventory != null) inventory.setChanged(); } - computer.setLevel(level); + return computer; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModemPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModemPeripheral.java index 2d6f329cd..a55c7fa16 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModemPeripheral.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModemPeripheral.java @@ -25,7 +25,7 @@ public class PocketModemPeripheral extends WirelessModemPeripheral { void setLocation(IPocketAccess access) { var entity = access.getEntity(); if (entity != null) { - level = entity.getCommandSenderWorld(); + level = entity.level(); position = entity.getEyePosition(1); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 3999149c3..d36bfd6f2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -9,8 +9,9 @@ import dan200.computercraft.api.lua.*; import dan200.computercraft.api.turtle.TurtleCommand; import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.metrics.Metrics; +import dan200.computercraft.core.metrics.MetricsObserver; +import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods; import dan200.computercraft.shared.turtle.core.*; @@ -64,11 +65,11 @@ import java.util.Optional; * @cc.since 1.3 */ public class TurtleAPI implements ILuaAPI { - private final IAPIEnvironment environment; + private final MetricsObserver metrics; private final TurtleAccessInternal turtle; - public TurtleAPI(IAPIEnvironment environment, TurtleAccessInternal turtle) { - this.environment = environment; + public TurtleAPI(ServerComputer computer, TurtleAccessInternal turtle) { + this.metrics = computer.getMetrics(); this.turtle = turtle; } @@ -78,7 +79,7 @@ public class TurtleAPI implements ILuaAPI { } private MethodResult trackCommand(TurtleCommand command) { - environment.observe(Metrics.TURTLE_OPS); + metrics.observe(Metrics.TURTLE_OPS); return turtle.executeCommand(command); } @@ -169,7 +170,7 @@ public class TurtleAPI implements ILuaAPI { */ @LuaFunction public final MethodResult dig(Optional side) { - environment.observe(Metrics.TURTLE_OPS); + metrics.observe(Metrics.TURTLE_OPS); return trackCommand(TurtleToolCommand.dig(InteractDirection.FORWARD, side.orElse(null))); } @@ -184,7 +185,7 @@ public class TurtleAPI implements ILuaAPI { */ @LuaFunction public final MethodResult digUp(Optional side) { - environment.observe(Metrics.TURTLE_OPS); + metrics.observe(Metrics.TURTLE_OPS); return trackCommand(TurtleToolCommand.dig(InteractDirection.UP, side.orElse(null))); } @@ -199,7 +200,7 @@ public class TurtleAPI implements ILuaAPI { */ @LuaFunction public final MethodResult digDown(Optional side) { - environment.observe(Metrics.TURTLE_OPS); + metrics.observe(Metrics.TURTLE_OPS); return trackCommand(TurtleToolCommand.dig(InteractDirection.DOWN, side.orElse(null))); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java index 142bdd4d2..220e99ab5 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java @@ -84,7 +84,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba getFamily(), Config.turtleTermWidth, Config.turtleTermHeight ); - computer.addAPI(new TurtleAPI(computer.getAPIEnvironment(), brain)); + computer.addAPI(new TurtleAPI(computer, brain)); brain.setupComputer(computer); return computer; } 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 d5c91da3f..7e8781793 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 @@ -266,8 +266,7 @@ public class TurtleBrain implements TurtleAccessInternal { newTurtle.transferStateFrom(oldOwner); var computer = newTurtle.createServerComputer(); - computer.setLevel((ServerLevel) world); - computer.setPosition(pos); + computer.setPosition((ServerLevel) world, pos); // Remove the old turtle oldWorld.removeBlock(oldPos, false); @@ -586,7 +585,7 @@ public class TurtleBrain implements TurtleAccessInternal { // If we've got a computer, ensure that we're allowed to perform work. var computer = owner.getServerComputer(); - if (computer != null && !computer.getComputer().getMainThreadMonitor().canWork()) return; + if (computer != null && !computer.getMainThreadMonitor().canWork()) return; // Pull a new command var nextCommand = commandQueue.poll(); @@ -599,7 +598,7 @@ public class TurtleBrain implements TurtleAccessInternal { // Dispatch the callback if (computer == null) return; - computer.getComputer().getMainThreadMonitor().trackWork(end - start, TimeUnit.NANOSECONDS); + computer.getMainThreadMonitor().trackWork(end - start, TimeUnit.NANOSECONDS); var callbackID = nextCommand.callbackID(); if (callbackID < 0) return; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 60f578b7e..dd12e9829 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -60,7 +60,7 @@ public final class TurtlePlayer { var player = brain.cachedPlayer; if (player == null || player.player.getGameProfile() != getProfile(access.getOwningPlayer()) - || player.player.getCommandSenderWorld() != access.getLevel()) { + || player.player.level() != access.getLevel()) { player = brain.cachedPlayer = create(brain); } else { player.setState(access); diff --git a/projects/fabric/src/main/resources/fabric.mod.json b/projects/fabric/src/main/resources/fabric.mod.json index f110a9f5a..b76fe9dbf 100644 --- a/projects/fabric/src/main/resources/fabric.mod.json +++ b/projects/fabric/src/main/resources/fabric.mod.json @@ -27,7 +27,7 @@ "dan200.computercraft.data.FabricDataGenerators" ], "jei_mod_plugin": [ - "dan200.computercraft.shared.integration.jei.JEIComputerCraft" + "dan200.computercraft.client.integration.jei.JEIComputerCraft" ], "rei_client": [ "dan200.computercraft.client.integration.rei.REIComputerCraft"