diff --git a/README.md b/README.md index 4e29dd5..ad004c6 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ Some special variables are provided: - `point` - point in the world you're pointing at with your crosshair - `this_obj` - entity you're pointing at (can be `nil`) +Do `/eval help` to get a quick reminder of these vars and functions. + ### Better output for arrays and some `userdata` objects ``` > me @@ -135,3 +137,20 @@ already existed. > cmd_eval | nil -- wiped for real ``` + +### Other shortcuts + +#### `dir()` and `keys()` + +List keys of the table (useful for exploring data structures, without flooding your chat). + +#### `get_objects_inside_radius()` shortcuts: `goir()` and `oir()` + +`goir(radius)` returns a list of objects around you +`oir(radius)` returns an iterator of objects around you + +``` +> goir(100) -- return a list of objects within 100 units around you + +> for v in oir(100) do print((v:get_luaentity() or {}).name) end +``` diff --git a/dump.lua b/dump.lua index 670ad21..b1b2518 100644 --- a/dump.lua +++ b/dump.lua @@ -119,4 +119,35 @@ local function repl_dump(o, indent, nested, level) return "{"..table.concat(ret, ", ").."}" end -return repl_dump + +local function dump_dir(o) + -- dump only top-level key = value pairs + local t = type(o) + if t ~= "table" then + return basic_dump(o) + end + + local ret = {} + local dumped_indexes = {} + for i, v in ipairs(o) do + ret[#ret + 1] = string.format("[%s] = %s", i, basic_dump(v)) + dumped_indexes[i] = true + end + for k, v in pairs(o) do + if not dumped_indexes[k] then + if type(k) ~= "string" or not is_valid_identifier(k) then + k = "["..basic_dump(k).."]" + end + v = basic_dump(v) + ret[#ret + 1] = k.." = "..v + end + end + return "{\n "..table.concat(ret, ",\n ").."\n}" +end + +local dump_funcs = { + repl_dump = repl_dump, + dump_dir = dump_dir, +} + +return dump_funcs diff --git a/init.lua b/init.lua index 41ea8c0..c7edac9 100644 --- a/init.lua +++ b/init.lua @@ -2,7 +2,9 @@ local MODNAME = core.get_current_modname() local MODPATH = core.get_modpath(MODNAME) local util = dofile(MODPATH .. DIR_DELIM .. "util.lua") -local repl_dump = dofile(MODPATH .. DIR_DELIM .. "dump.lua") +local dump_funcs = dofile(MODPATH .. DIR_DELIM .. "dump.lua") +local repl_dump = dump_funcs.repl_dump +local dump_dir = dump_funcs.dump_dir local api = {} _G[MODNAME] = api @@ -55,6 +57,25 @@ local function create_shared_environment(player_name) end return vector.round(me:get_pos()) end, + help = function() + local msg = [[ +# Variables: +me -- your player object +my_pos -- your position +here -- your position where command was executed at (does not change) +point -- the exact pos you're pointing with the crosshair +this_obj -- the obj you're pointing at with the crosshair or nil +this_node_pos -- the node position you're pointing at +global -- actual global environment (same as _G) + +# Functions: +dir(t) -- print table key/values (returns nothing) +keys(t) -- print table keys (returns nothing) +goir(radius) -- return list of objects around you +oir(radius) -- return iterator for objects around you +]] + core.chat_send_player(player_name, msg) + end, } local g = {} -- "do not warn again" flags @@ -75,8 +96,12 @@ local function create_shared_environment(player_name) local eval_env = setmetatable( { + --global = _G, -- this works, but dumps whole global env if you just print `cmd_eval` value + _G = global_proxy, -- use our proxy to get warnings + global = global_proxy, -- just a different name for globals my_name = player_name, print = function(...) + -- print to chat, instead of console local msg = '< ' for i = 1, select('#', ...) do if i > 1 then msg = msg .. '\t' end @@ -84,10 +109,58 @@ local function create_shared_environment(player_name) end core.chat_send_player(player_name, msg) end, - --global = _G, -- this works, but dumps whole global env if you just print `cmd_eval` value - _G = global_proxy, -- use our proxy to get warnings - global = global_proxy, -- just a different name for globals + dir = function(o) + core.chat_send_player(player_name, dump_dir(o)) + end, + keys = function(o) + -- collect all keys of the table, no values + if type(o) == "table" then + local keys = {} + for k, _ in pairs(o) do + local key = k + local t = type(key) + if t == "string" then + key = '"' .. key .. '"' + elseif t == "number" then + key = '[' .. key .. ']' + else + key = '[' .. tostring(key) .. ']' + end + table.insert(keys, key) + end + table.sort(keys) + core.chat_send_player(player_name, table.concat(keys, ',\n')) + else + core.chat_send_player(player_name, string.format("Not a table: %s", dump(t))) + end + end, --dump = repl_dump, + goir = function(radius) + local me = core.get_player_by_name(player_name) + if me then + local objs = core.get_objects_inside_radius(me:get_pos(), radius) + return objs + else + return {} + end + end, + oir = function(radius) + local me = core.get_player_by_name(player_name) + if me then + local objs = core.get_objects_inside_radius(me:get_pos(), radius) + local nextkey, v + --local i = 1 + return function() + -- FIXME skip invalid objects here? + nextkey, v = next(objs, nextkey) + return v + -- i = i + 1 + -- return objs[i] + end + else + return function() return nil end + end + end, }, { __index = function(self, key) @@ -123,6 +196,9 @@ local function create_command_environment(player_name) local me = core.get_player_by_name(player_name) local here = me:get_pos() local cmd_env = { + -- This is a special _per-command_ environment. + -- The rationale is: each command should have it's own "here" + -- It may matter when we start long-running tasks or something. here = here, } setmetatable(