diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index 41d2942d..a95d66be 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -31,67 +31,75 @@ Global.register(interface_env, function(tbl) interface_env = tbl end) ---- Adds a callback function when the interface command is used --- nb: returned value is saved in the env that the interface uses --- @tparam string name the name that the value is loaded under, cant use upvalues --- @tparam function callback the function that will run whent he command is used +--- 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 --- this is a meta function for __index when self[key] is nil +--- 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 innovation to be ran and returns the result. +--- Sends an invocation to be ran and returns the result. -- @command interface --- @tparam string innovation the command that will be run -Commands.new_command('interface', 'Sends an innovation to be ran and returns the result.') -:add_param('innovation', false) +-- @tparam string invocation the command that will be run +Commands.new_command('interface', 'Sends an invocation to be ran and returns the result.') +:add_param('invocation', false) :enable_auto_concat() :set_flag('admin_only') -:register(function(player, innovation) - if not innovation:find('%s') and not innovation:find('return') then - -- if there are no spaces and return is not present then return is appended to the start - innovation='return '..innovation +: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 - -- temp_env will index to interface_env and interface_modules if value not found - local temp_env = setmetatable({}, {__index=get_index}) - if player then -- player can be nil when it is the server + + -- _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 - -- loops over callbacks and loads the values returned local _, rtn = pcall(callback, player) - temp_env[name]=rtn + rawset(_env, name, rtn) end end - -- sets the global metatable to prevent new values being made - -- global will index to temp_env and new indexes saved to interface_sandbox - local old_mt = getmetatable(_G) - setmetatable(_G, {__index=temp_env, __newindex=interface_env}) - -- runs the innovation and returns values to the player - innovation = loadstring(innovation) - local success, rtn = pcall(innovation) - setmetatable(_G, old_mt) + + -- 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 + + -- Run the invocation + local success, rtn = pcall(invocation_func) if not success then - if type(rtn) == 'string' then - -- there may be stack trace that must be removed to avoid desyncs - rtn = rtn:gsub('%.%.%..-/temp/currently%-playing', '') - end - return Commands.error(rtn) - else - return Commands.success(rtn) + local err = rtn:gsub('%.%.%..-/temp/currently%-playing', '') + return Commands.error(err) end + return Commands.success(rtn) end) --- adds some basic callbacks for the interface +-- 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) @@ -99,9 +107,10 @@ 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_callback=add_interface_callback, - interface_env=interface_env, - interface_callbacks=interface_callbacks, - clean_stack_trace=function(str) return str:gsub('%.%.%..-/temp/currently%-playing', '') end + 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 } \ No newline at end of file