--[[-- Commands - Rcon System command which runs arbitrary code within a custom (not sandboxed) environment --- Get the names of all online players, using rcon /_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; return names; --- Get the names of all online players, using clustorio ipcs /_system-rcon local names = {}; for index, player in pairs(game.connected_player) do names[index] = player.name end; ipc("online-players", names); ]] local ExpUtil = require("modules/exp_util") local Async = require("modules/exp_util/async") local Storage = require("modules/exp_util/storage") local Clustorio = require("modules/clusterio/api") local Commands = require("modules/exp_commands") --- @class Commands local rcon_env = {} --- @type table local rcon_static = {} --- @type table local rcon_dynamic = {} --- @type table setmetatable(rcon_static, { __index = _G }) setmetatable(rcon_env, { __index = rcon_static }) --- Some common static values which can be added now --- @diagnostic disable: name-style-check rcon_static.Async = Async rcon_static.ExpUtil = ExpUtil rcon_static.Commands = Commands rcon_static.Clustorio = Clustorio rcon_static.print = Commands.print rcon_static.ipc = Clustorio.send_json --- @diagnostic enable: name-style-check --- Some common callback values which are useful when a player uses the command --- @alias ExpCommand.RconDynamic fun(player: LuaPlayer?): any function rcon_dynamic.player(player) return player end function rcon_dynamic.surface(player) return player and player.surface end function rcon_dynamic.force(player) return player and player.force end function rcon_dynamic.position(player) return player and player.position end function rcon_dynamic.entity(player) return player and player.selected end function rcon_dynamic.tile(player) return player and player.surface.get_tile(player.position.x, player.position.y) end --- The rcon env is saved between command runs to prevent desyncs Storage.register(rcon_env, function(tbl) rcon_env = setmetatable(tbl, { __index = rcon_static }) end) --- Static values can be added to the rcon env which are not stored in global such as modules --- @param name string Name of the value as it will appear in the rcon environment --- @param value any Value it is have function Commands.add_rcon_static(name, value) ExpUtil.assert_not_runtime() rcon_static[name] = value end --- Callback values can be added to the rcon env, these are called on each invocation and should return one value --- @param name string Name of the value as it will appear in the rcon environment --- @param callback ExpCommand.RconDynamic Callback called to get the current value function Commands.add_rcon_dynamic(name, callback) ExpUtil.assert_not_runtime() rcon_dynamic[name] = callback end Commands.new("_rcon", { "exp-commands_rcon.description" }) :argument("invocation", { "exp-commands_rcon.arg-invocation" }, Commands.types.string) :enable_auto_concatenation() :add_flags{ "system_only" } :register(function(player, invocation_string) --- @cast invocation_string string -- Construct the environment the command will run within local env = setmetatable({}, { __index = rcon_env, __newindex = rcon_env }) for name, callback in pairs(rcon_dynamic) do local _, rtn = pcall(callback, player.index > 0 and player or nil) rawset(env, name, rtn) end -- Compile and run the invocation string local invocation, compile_error = load(invocation_string, "rcon-invocation", "t", env) if compile_error then return Commands.status.invalid_input(compile_error) else --- @cast invocation -nil local success, rtn = xpcall(invocation, debug.traceback) if success == false then return Commands.status.error(rtn) else return Commands.status.success(rtn) end end end)