· 4 years ago · Jul 09, 2021, 08:08 AM
1--- The commands API allows your system to directly execute [Minecraft
2-- commands][mc] and gather data from the results.
3--
4-- While one may use @{commands.exec} directly to execute a command, the
5-- commands API also provides helper methods to execute every command. For
6-- instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`.
7--
8-- @{commands.async} provides a similar interface to execute asynchronous
9-- commands. `commands.async.say("Hi!")` is equivalent to
10-- `commands.execAsync("Hi!")`.
11--
12-- [mc]: https://minecraft.gamepedia.com/Commands
13--
14-- @module commands
15-- @usage Set the block above this computer to stone:
16--
17-- commands.setblock("~", "~1", "~", "minecraft:stone")
18
19if not commands then
20 error("Cannot load command API on normal computer", 2)
21end
22
23--- The builtin commands API, without any generated command helper functions
24--
25-- This may be useful if a built-in function (such as @{commands.list}) has been
26-- overwritten by a command.
27local native = commands.native or commands
28
29local function collapseArgs(bJSONIsNBT, ...)
30 local args = table.pack(...)
31 for i = 1, #args do
32 local arg = args[i]
33 if type(arg) == "boolean" or type(arg) == "number" or type(arg) == "string" then
34 args[i] = tostring(arg)
35 elseif type(arg) == "table" then
36 args[i] = textutils.serialiseJSON(arg, bJSONIsNBT)
37 else
38 error("Expected string, number, boolean or table", 3)
39 end
40 end
41
42 return table.concat(args, " ")
43end
44
45-- Put native functions into the environment
46local env = _ENV
47env.native = native
48for k, v in pairs(native) do
49 env[k] = v
50end
51
52-- Create wrapper functions for all the commands
53local tAsync = {}
54local tNonNBTJSONCommands = {
55 ["tellraw"] = true,
56 ["title"] = true,
57}
58
59local command_mt = {}
60function command_mt.__call(self, ...)
61 local meta = self[command_mt]
62 local sCommand = collapseArgs(meta.json, table.concat(meta.name, " "), ...)
63 return meta.func(sCommand)
64end
65
66function command_mt.__tostring(self)
67 local meta = self[command_mt]
68 return ("command %q"):format("/" .. table.concat(meta.name, " "))
69end
70
71local function mk_command(name, json, func)
72 return setmetatable({
73 [command_mt] = {
74 name = name,
75 func = func,
76 json = json,
77 },
78 }, command_mt)
79end
80
81function command_mt.__index(self, key)
82 local meta = self[command_mt]
83 if meta.children then return nil end
84 meta.children = true
85
86 local name = meta.name
87 for _, child in ipairs(native.list(table.unpack(name))) do
88 local child_name = { table.unpack(name) }
89 child_name[#child_name + 1] = child
90 self[child] = mk_command(child_name, meta.json, meta.func)
91 end
92
93 return self[key]
94end
95
96for _, sCommandName in ipairs(native.list()) do
97 if env[sCommandName] == nil then
98 local bJSONIsNBT = tNonNBTJSONCommands[sCommandName] == nil
99 env[sCommandName] = mk_command({ sCommandName }, bJSONIsNBT, native.exec)
100 tAsync[sCommandName] = mk_command({ sCommandName }, bJSONIsNBT, native.execAsync)
101 end
102end
103
104--- A table containing asynchronous wrappers for all commands.
105--
106-- As with @{commands.execAsync}, this returns the "task id" of the enqueued
107-- command.
108-- @see execAsync
109-- @usage Asynchronously sets the block above the computer to stone.
110--
111-- commands.async.setblock("~", "~1", "~", "minecraft:stone")
112env.async = tAsync