From 571ea794a84aea66eee81eb51d9edab47d13e4cb Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 22 Jun 2024 17:18:21 +0100 Subject: [PATCH] Decouple CommandAPI from the command computer BE All we really need for its implementation is a level and position, which we can get directly from the server block entity! --- .../shared/computer/apis/CommandAPI.java | 77 +++++++++++++++---- .../blocks/CommandComputerBlockEntity.java | 66 +--------------- 2 files changed, 64 insertions(+), 79 deletions(-) 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 fa841cf1d..0ff8f5e8f 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,15 +53,14 @@ 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 result = commandManager.performPrefixedCommand(computer.getSource(), command); + var result = commandManager.performPrefixedCommand(getSource(), command); return new Object[]{ result > 0, receiver.copyOutput(), result }; } catch (Throwable t) { LOG.error(Logging.JAVA_ERROR, "Error running command.", t); @@ -134,7 +138,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); @@ -161,7 +164,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() }; } @@ -186,7 +189,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
@@ -220,7 +222,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");
         }
 
@@ -265,10 +267,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");
@@ -278,4 +279,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/CommandComputerBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/CommandComputerBlockEntity.java
index 1f831574f..c7f5c6d40 100644
--- 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
@@ -8,85 +8,21 @@ 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));
+        computer.addAPI(new CommandAPI(computer));
         return computer;
     }