292 lines
7.4 KiB
Markdown
292 lines
7.4 KiB
Markdown
## Adds `/eval` command
|
|
|
|
`/eval` takes lua code as argument and executes it. It will echo your
|
|
command and show it's output and returned value. Each player gets
|
|
their own "global" environment so they can't interfere with other
|
|
user's envs by accident (exposed as `cmd_eval.e[player_name]`).
|
|
|
|
Type `/eval help` to see the built-in help.
|
|
|
|
## Some nice features:
|
|
### Expression/statement agnostic
|
|
This command:
|
|
```
|
|
/eval 1+2
|
|
```
|
|
Outputs this:
|
|
```
|
|
> 1+2
|
|
| 3
|
|
```
|
|
|
|
This also just works:
|
|
```
|
|
> x = 2*2
|
|
|
|
> x
|
|
| 4
|
|
```
|
|
|
|
Multiple values also work:
|
|
|
|
```
|
|
> return 1,nil,3
|
|
| 1,
|
|
| nil,
|
|
| 3
|
|
```
|
|
|
|
### Backtrace and error output
|
|
Outputs both the error and clean backtrace (stack related only to provided code)
|
|
|
|
### Print function
|
|
No need to use `core.chat_send_player()`, just use `print()` - it will do the right thing.
|
|
|
|
```
|
|
> print(here)
|
|
< (-98.0, 15.5, 33.4)
|
|
```
|
|
|
|
```
|
|
> objs = core.get_objects_inside_radius(here, 10)
|
|
|
|
> for i,o in ipairs(objs) do print(i, (o:get_luaentity() or {}).name, o:get_pos()) end
|
|
< 1 nil (-98.0, 15.5, 33.4)
|
|
< 2 mobs_animal:kitten (-101.0, 16.5, 30.0)
|
|
< 3 mobs_animal:chicken (-92.0, 15.5, 34.2)
|
|
```
|
|
|
|
### "Magic" variables
|
|
Some special variables are provided:
|
|
- `here` - position where you executed the command
|
|
- `me` - your player object
|
|
- `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
|
|
| #<player: "singleplayer">
|
|
```
|
|
|
|
Show indices for easier manual access:
|
|
```
|
|
> core.get_objects_inside_radius(here, 10)
|
|
| {
|
|
| [1] = #<player: "singleplayer">,
|
|
| [2] = #<luaentity: "mobs_animal:kitten">,
|
|
| [3] = #<luaentity: "mobs_animal:chicken">,
|
|
| }
|
|
```
|
|
|
|
### Keeping result of last /eval in `_` variable
|
|
```
|
|
> core.get_objects_inside_radius(here, 10)
|
|
| {
|
|
| [1] = #<player: "singleplayer">,
|
|
| [2] = #<luaentity: "mobs_animal:kitten">,
|
|
| }
|
|
|
|
> _[2]
|
|
| #<luaentity: "mobs_animal:kitten">
|
|
|
|
> pos = _:get_pos()
|
|
|
|
> pos
|
|
| {
|
|
| x = 123.5,
|
|
| y = 15.0,
|
|
| z = 68.4
|
|
| }
|
|
```
|
|
|
|
### Accessing and changing global variables
|
|
You can save some keypresses by not typing `local` in front of every
|
|
variable. Assigning to globals won't clobber them, instead using your personal
|
|
environment. But you can still read globals as usual.
|
|
|
|
```
|
|
> print(myvar)
|
|
* Accessing undeclared variable: "myvar"
|
|
< nil
|
|
|
|
> myvar = 1
|
|
| Done.
|
|
|
|
> print(myvar)
|
|
< 1
|
|
| Done.
|
|
```
|
|
|
|
If you want to create a new global or actually overwrite existing one,
|
|
you can access global environment through usual `_G` or `global`
|
|
variables. Using them will print a message telling you if global var
|
|
already existed.
|
|
|
|
```
|
|
> cmd_eval = nil
|
|
| Done.
|
|
|
|
> cmd_eval -- accesses real global
|
|
{
|
|
e = { ... }, ...
|
|
}
|
|
|
|
> global.cmd_eval = nil
|
|
* Overwriting global: "cmd_eval"
|
|
| Done.
|
|
|
|
> 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
|
|
```
|
|
|
|
### Coroutine support
|
|
|
|
You can call `yield(...)` inside the code you're evaluating. The
|
|
yielded value will be displayed, same as normal returned value, but
|
|
you will also be able to resume the computation by typing
|
|
`/eval_resume`:
|
|
|
|
```
|
|
> for i=1,3 do yield(i) end
|
|
| 1
|
|
* coroutine suspended, type /eval_resume to continue
|
|
/eval_resume
|
|
* resuming...
|
|
| 2
|
|
* coroutine suspended, type /eval_resume to continue
|
|
/eval_resume
|
|
* resuming...
|
|
| 3
|
|
* coroutine suspended, type /eval_resume to continue
|
|
/eval_resume
|
|
| Done.
|
|
/eval_resume
|
|
* resuming...
|
|
* Nothing to resume
|
|
```
|
|
|
|
This can allow you for some shortcuts, for example, visiting all players:
|
|
|
|
```
|
|
/eval for p in players do me:move_to(p:get_pos()); yield() end
|
|
```
|
|
|
|
type `/eval_resume` to visit next one.
|
|
|
|
Keep in mind that while the coroutine is paused, it's enviroment can
|
|
change (tables can be modified, some object refs can become invalid etc.)
|
|
|
|
#### An example of using yield()
|
|
|
|
Evaluate this while pointing at the first position:
|
|
|
|
`/eval a = point; yield(); b = point; vizlib.draw_line(a,b); print(vector.distance(a,b))`
|
|
|
|
Then point at the second position, and type `/eval_resume`.
|
|
This will draw a line between 2 points and print the distance between them.
|
|
|
|
|
|
### Formspec output/input
|
|
|
|
#### `fsdump(value)`
|
|
|
|
Evaluate `fsdump(value)` to show the value in a formspec instead of
|
|
chat window. This will allow you to select and copy the dumped text,
|
|
and also keep your chat history from getting spammed. You can call
|
|
this multiple times, inside a loop, etc. The computation will be
|
|
paused (see "coroutine support").
|
|
|
|
If you close the dump window with ESC or `x` button, the computation
|
|
will remain paused, and can be resumed by typing `/eval_resume`. If
|
|
you push `resume` formspec button, it will be resumed normally.
|
|
|
|
##### Example using `fsdump()`
|
|
|
|
Inspecting lots of objects around you by visiting and dumping them:
|
|
|
|
`/eval for o in oir(30) do the_obj = o; me:move_to(o:get_pos()); fsdump(o:get_luaentity()); end`
|
|
|
|
This command will teleport you to position of some entity nearby and
|
|
open a formspec with the dump of it's lua table. After examining the
|
|
values, you can press the "resume" formspec button to resume the
|
|
evaluation and go to the next entity. We stored current entity in a
|
|
variable, so we can reference it when we paused. If you just closed
|
|
the formspec instead of pressing "resume", you can sill continue
|
|
execution by typing `/eval_resume`.
|
|
|
|
NOTE: `cmd_eval` currently supports only single coroutine, for
|
|
simplicity reasons; this means that executing `/eval` while paused,
|
|
will overwrite your current coroutine with a new one.
|
|
|
|
#### `fsinput(label, text)`
|
|
|
|
Evaluate t`fsinput()` (arguments are optional) to open a formspec
|
|
where you can input some text. The text you enter will be passed back
|
|
to the coroutine as a return value of the `fsinput()` call.
|
|
|
|
For example:
|
|
|
|
`/eval core.get_meta(under):set_string('infotext',fsinput())`
|
|
|
|
Will open a window that will let you to edit the `infotext` meta field
|
|
of the node you're pointing at.
|
|
|
|
Or, same thing, but fancier, showing existing infotext in a formspec:
|
|
|
|
`/eval local m = core.get_meta(under); m:set_string('infotext',fsinput('infotext', m:get_string('infotext')))`
|
|
|
|
If you close `fsinput()` formspec without pusing `send` button, the
|
|
computation _will not_ be resumed.
|
|
|
|
You can still resume it by typing `/eval_resume <text>` - the argument
|
|
text will be passed instead of the context of text area of the formspec.
|
|
|
|
|
|
### More utilities
|
|
|
|
#### Binary data
|
|
|
|
##### `ascii()` and `unascii()`
|
|
|
|
Have you ever encountered "<invalid UTF-8 string>" somewhere with no
|
|
easy way to display it?
|
|
|
|
`ascii()` will allow you to inspect these strings (which may be just
|
|
some broken unicode or arbitrary binary data).
|
|
|
|
`ascii(str)` will escape all control sequences and unprintable
|
|
characters so you can output it normally. It works well with broken
|
|
strings, because printable ASCII characters will be kept unchanged.
|
|
|
|
The reverse of this is `unascii(str)`, which will take the escaped
|
|
sequence and turn it back into raw bytes. This supports only `\xFF`
|
|
hex escapes, but it works well enough in pair with `ascii()`.
|
|
|
|
|
|
### Resetting your personal environment
|
|
|
|
If your environment gets messed up, or you just want to get rid of the
|
|
variables you've stored, you can type:
|
|
|
|
`/eval_reset`
|