121 lines
3.5 KiB
Lua
121 lines
3.5 KiB
Lua
-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe
|
|
--
|
|
-- SPDX-License-Identifier: LicenseRef-CCPL
|
|
|
|
--[[- Execute [Minecraft commands][mc] and gather data from the results from
|
|
a command computer.
|
|
|
|
> [!NOTE]
|
|
> This API is only available on Command computers. It is not accessible to normal
|
|
> players.
|
|
|
|
While one may use [`commands.exec`] directly to execute a command, the
|
|
commands API also provides helper methods to execute every command. For
|
|
instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`.
|
|
|
|
[`commands.async`] provides a similar interface to execute asynchronous
|
|
commands. `commands.async.say("Hi!")` is equivalent to
|
|
`commands.execAsync("say Hi!")`.
|
|
|
|
[mc]: https://minecraft.wiki/w/Commands
|
|
|
|
@module commands
|
|
@usage Set the block above this computer to stone:
|
|
|
|
commands.setblock("~", "~1", "~", "minecraft:stone")
|
|
]]
|
|
if not commands then
|
|
error("Cannot load command API on normal computer", 2)
|
|
end
|
|
|
|
--- The builtin commands API, without any generated command helper functions
|
|
--
|
|
-- This may be useful if a built-in function (such as [`commands.list`]) has been
|
|
-- overwritten by a command.
|
|
local native = commands.native or commands
|
|
|
|
local function collapseArgs(bJSONIsNBT, ...)
|
|
local args = table.pack(...)
|
|
for i = 1, #args do
|
|
local arg = args[i]
|
|
if type(arg) == "boolean" or type(arg) == "number" or type(arg) == "string" then
|
|
args[i] = tostring(arg)
|
|
elseif type(arg) == "table" then
|
|
args[i] = textutils.serialiseJSON(arg, bJSONIsNBT)
|
|
else
|
|
error("Expected string, number, boolean or table", 3)
|
|
end
|
|
end
|
|
|
|
return table.concat(args, " ")
|
|
end
|
|
|
|
-- Put native functions into the environment
|
|
local env = _ENV
|
|
env.native = native
|
|
for k, v in pairs(native) do
|
|
env[k] = v
|
|
end
|
|
|
|
-- Create wrapper functions for all the commands
|
|
local tAsync = {}
|
|
local tNonNBTJSONCommands = {
|
|
["tellraw"] = true,
|
|
["title"] = true,
|
|
}
|
|
|
|
local command_mt = {}
|
|
function command_mt.__call(self, ...)
|
|
local meta = self[command_mt]
|
|
local sCommand = collapseArgs(meta.json, table.concat(meta.name, " "), ...)
|
|
return meta.func(sCommand)
|
|
end
|
|
|
|
function command_mt.__tostring(self)
|
|
local meta = self[command_mt]
|
|
return ("command %q"):format("/" .. table.concat(meta.name, " "))
|
|
end
|
|
|
|
local function mk_command(name, json, func)
|
|
return setmetatable({
|
|
[command_mt] = {
|
|
name = name,
|
|
func = func,
|
|
json = json,
|
|
},
|
|
}, command_mt)
|
|
end
|
|
|
|
function command_mt.__index(self, key)
|
|
local meta = self[command_mt]
|
|
if meta.children then return nil end
|
|
meta.children = true
|
|
|
|
local name = meta.name
|
|
for _, child in ipairs(native.list(table.unpack(name))) do
|
|
local child_name = { table.unpack(name) }
|
|
child_name[#child_name + 1] = child
|
|
self[child] = mk_command(child_name, meta.json, meta.func)
|
|
end
|
|
|
|
return self[key]
|
|
end
|
|
|
|
for _, sCommandName in ipairs(native.list()) do
|
|
if env[sCommandName] == nil then
|
|
local bJSONIsNBT = tNonNBTJSONCommands[sCommandName] == nil
|
|
env[sCommandName] = mk_command({ sCommandName }, bJSONIsNBT, native.exec)
|
|
tAsync[sCommandName] = mk_command({ sCommandName }, bJSONIsNBT, native.execAsync)
|
|
end
|
|
end
|
|
|
|
--- A table containing asynchronous wrappers for all commands.
|
|
--
|
|
-- As with [`commands.execAsync`], this returns the "task id" of the enqueued
|
|
-- command.
|
|
-- @see execAsync
|
|
-- @usage Asynchronously sets the block above the computer to stone.
|
|
--
|
|
-- commands.async.setblock("~", "~1", "~", "minecraft:stone")
|
|
env.async = tAsync
|