Files
factorio-scenario-ExpCluster/exp_legacy/module/modules/commands/interface.lua
2024-11-19 22:35:22 +00:00

118 lines
4.6 KiB
Lua

--[[-- Commands Module - Interface
- Adds a command that acts as a direct link to the the active softmod, for debug use
@commands Interface
]]
local ExpUtil = require("modules/exp_util")
local Storage = require("modules/exp_util/storage")
local Commands = require("modules.exp_legacy.expcore.commands") --- @dep expcore.commands
-- modules that are loaded into the interface env to be accessed
local interface_modules = {
["Commands"] = Commands,
["output"] = Commands.print,
["Group"] = "modules.exp_legacy.expcore.permission_groups",
["Roles"] = "modules.exp_legacy.expcore.roles",
["Gui"] = "modules.exp_legacy.expcore.gui",
["Datastore"] = "modules.exp_legacy.expcore.datastore",
["External"] = "modules.exp_legacy.expcore.external",
}
-- loads all the modules given in the above table
for key, value in pairs(interface_modules) do
if type(value) == "string" then
interface_modules[key] = ExpUtil.optional_require(value)
end
end
local interface_env = {} -- used as a persistent sandbox for interface commands
local interface_callbacks = {} -- saves callbacks which can load new values per use
Storage.register(interface_env, function(tbl)
interface_env = tbl
end)
--- Adds a static module that can be accessed with the interface
-- @tparam string name The name that the value is assigned to
-- @tparam any value The value that will be accessible in the interface env
-- callback param - player: LuaPlayer - the player who used the command
local function add_interface_module(name, value)
interface_modules[name] = value
end
--- Adds a dynamic value that is calculated when the interface is used
-- @tparam string name The name that the value is assigned to
-- @tparam function callback The function that will be called to get the value
local function add_interface_callback(name, callback)
if type(callback) == "function" then
interface_callbacks[name] = callback
end
end
--- Internal, this is a meta function for __index when self[key] is nil
local function get_index(_, key)
if interface_env[key] then
return interface_env[key]
elseif interface_modules[key] then
return interface_modules[key]
elseif _G[key] then
return _G[key]
end
end
--- Sends an invocation to be ran and returns the result.
-- @command interface
-- @tparam string invocation the command that will be run
Commands.new_command("interface", { "expcom-interface.description" }, "Sends an invocation to be ran and returns the result.")
:add_param("invocation", false)
:enable_auto_concat()
:set_flag("admin_only")
:register(function(player, invocation)
-- If the invocation has no white space then prepend return to it
if not invocation:find("%s") and not invocation:find("return") then
invocation = "return " .. invocation
end
-- _env will be the new _ENV that the invocation will run inside of
local _env = setmetatable({}, {
__index = get_index,
__newindex = interface_env,
})
-- If the command is ran by a player then load the dynamic values
if player then
for name, callback in pairs(interface_callbacks) do
local _, rtn = pcall(callback, player)
rawset(_env, name, rtn)
end
end
-- Compile the invocation with the custom _env value
local invocation_func, compile_error = load(invocation, "interface", nil, _env)
if compile_error then return Commands.error(compile_error) end
--- @cast invocation_func -nil
-- Run the invocation
local success, rtn = pcall(invocation_func)
if not success then
local err = rtn:gsub("%.%.%..-/temp/currently%-playing", "")
return Commands.error(err)
end
return Commands.success(rtn)
end)
-- Adds some basic callbacks for the interface
add_interface_callback("player", function(player) return player end)
add_interface_callback("surface", function(player) return player.surface end)
add_interface_callback("force", function(player) return player.force end)
add_interface_callback("position", function(player) return player.position end)
add_interface_callback("entity", function(player) return player.selected end)
add_interface_callback("tile", function(player) return player.surface.get_tile(player.position) end)
-- Module Return
return {
add_interface_module = add_interface_module,
add_interface_callback = add_interface_callback,
interface_env = interface_env,
clean_stack_trace = function(str) return str:gsub("%.%.%..-/temp/currently%-playing", "") end,
}