1 |
Progress I made so far:
|
1 |
Progress I made so far:
|
2 |
\n
|
2 |
\n
|
3 |
In @Sprung's examples a widget waits for a specific text command to run a specific code. I wanted a solution to run an arbitrary text command. So I found the loadstring() function which makes an executable command out of string (and this is when I found that Spring uses Lua 5.1, because it is load() in newer versions, and there are some other changes in Lua, and I need to switch from 4th to 2nd edition of PiL).
|
3 |
In @Sprung's examples a widget waits for a specific text command to run a specific code. I wanted a solution to run an arbitrary text command. So I found the loadstring() function which makes an executable command out of string (and this is when I found that Spring uses Lua 5.1, because it is load() in newer versions, and there are some other changes in Lua, and I need to switch from 4th to 2nd edition of PiL).
|
4 |
\n
|
4 |
\n
|
5 |
To filter the text commands I start them with 'sudo' (can be any string), like "/luaui sudo Spring.GetOrderToUnit(..)" and the widget filters it (see code below).
|
5 |
To filter the text commands I start them with 'sudo' (can be any string), like "/luaui sudo Spring.GetOrderToUnit(..)" and the widget filters it (see code below).
|
6 |
\n
|
6 |
\n
|
7 |
The problem with loadstring() is that the resulting command runs in global environment and does not have the access to all the local variables and functions of the widget. So, for example, if I want to use in my command UnitID of a selected unit, I have two options:
|
7 |
The problem with loadstring() is that the resulting command runs in global environment and does not have the access to all the local variables and functions of the widget. So, for example, if I want to use in my command UnitID of a selected unit, I have two options:
|
8 |
\n
|
8 |
\n
|
9 |
1. To make the widget print the UnitID and then to manually insert it into the command. The code then will be
|
9 |
1. To make the widget print the UnitID and then to manually insert it into the command. The code then will be
|
10 |
\n
|
10 |
\n
|
11 |
{{{function widget:TextCommand(str)
|
11 |
{{{function widget:TextCommand(str)
|
12 |
if string.find(str, "^sudo ") then -- if a string starts with "sudo "
|
12 |
if string.find(str, "^sudo ") then -- if a string starts with "sudo "
|
13 |
local code = string.sub( str, 5 ) -- get the rest of the string after "sudo "
|
13 |
local code = string.sub( str, 5 ) -- get the rest of the string after "sudo "
|
14 |
Echo( "Executing: " .. code )
|
14 |
Echo( "Executing: " .. code )
|
15 |
cmd = loadstring( code ) -- prepare a command but do not execute it yet
|
15 |
cmd = loadstring( code ) -- prepare a command but do not execute it yet
|
16 |
local ok,msg = pcall( cmd ) -- run the command with "protected call" so in a case of error the widget will not be removed
|
16 |
local ok,msg = pcall( cmd ) -- run the command with "protected call" so in a case of error the widget will not be removed
|
17 |
if not ok then Echo( "Error: " .. msg ) end -- in case of an error, print the error message
|
17 |
if not ok then Echo( "Error: " .. msg ) end -- in case of an error, print the error message
|
18 |
end
|
18 |
end
|
19 |
end}}}
|
19 |
end}}}
|
20 |
\n
|
20 |
\n
|
21 |
2. To prepare an environment for the cmd which will include the local stuff. This is the trickiest part, I tried to automate it with loops of debug.getlocal and debug.getupvalue, traversing along the stack levels, but couldn't find where the widget's locals are residing. So I came with a dirty solution to manually add the locals of interest to a special table and then to make the new environment out of it, adding the index of globals. The code is then:
|
21 |
2. To prepare an environment for the cmd which will include the local stuff. This is the trickiest part, I tried to automate it with loops of debug.getlocal and debug.getupvalue, traversing along the stack levels, but couldn't find where the widget's locals are residing. So I came with a dirty solution to manually add the locals of interest to a special table and then to make the new environment out of it, adding the index of globals. The code is then:
|
22 |
{{{function widget:TextCommand(str)
|
22 |
{{{function widget:TextCommand(str)
|
23 |
\n
|
23 |
\n
|
24 |
if string.find(str, "^sudo ") then -- if a string starts with "sudo "
|
24 |
if string.find(str, "^sudo ") then -- if a string starts with "sudo "
|
25 |
local code = string.sub( str, 5 ) -- get the rest of the string after "sudo "
|
25 |
local code = string.sub( str, 5 ) -- get the rest of the string after "sudo "
|
26 |
Echo( "Executing: " .. code )
|
26 |
Echo( "Executing: " .. code )
|
27 |
cmd = loadstring( code ) -- prepare a command but do not execute it yet
|
27 |
cmd = loadstring( code ) -- prepare a command but do not execute it yet
|
28 |
local env = getfenv(2) -- get the environment of the caller function (a widget handler?)
|
28 |
local env = getfenv(2) -- get the environment of the caller function (a widget handler?)
|
29 |
local locals = { z = z, selectedUnits = selectedUnits } -- put here all the local variables you want access to
|
29 |
local locals = { z = z, selectedUnits = selectedUnits } -- put here all the local variables you want access to
|
30 |
setfenv(cmd, setmetatable(locals, { __index = env._G })) -- set environment for the command, including both the locals and the global index
|
30 |
setfenv(cmd, setmetatable(locals, { __index = env._G })) -- set environment for the command, including both the locals and the global index
|
31 |
local ok,msg = pcall( cmd ) -- run the command with "protected call" so in a case of error the widget will not be removed
|
31 |
local ok,msg = pcall( cmd ) -- run the command with "protected call" so in a case of error the widget will not be removed
|
32 |
if not ok then Echo( "Error: " .. msg ) end -- in case of an error, print the error message
|
32 |
if not ok then Echo( "Error: " .. msg ) end -- in case of an error, print the error message
|
33 |
end
|
33 |
end
|
34 |
end}}}
|
34 |
end}}}
|
35 |
\n
|
35 |
\n
|
36 |
With the latter, I could run things like {{{/luaui sudo Spring.Echo( z )}}} and {{{/luaui sudo Spring.Echo( Spring.GetUnitMaxRange( selectedUnits[1] ))}}}
|
36 |
With the latter, I could run things like {{{/luaui sudo Spring.Echo( z )}}} and {{{/luaui sudo Spring.Echo( Spring.GetUnitMaxRange( selectedUnits[1] ))}}}
|
37 |
\n
|
37 |
\n
|
38 |
and to get in the console
|
38 |
and to get in the console
|
39 |
\n
|
39 |
\n
|
40 |
{{{Executing: Spring.Echo( Spring.GetUnitMaxRange( selectedUnits[1] ))
|
40 |
{{{Executing: Spring.Echo( Spring.GetUnitMaxRange( selectedUnits[1] ))
|
41 |
600.000061
|
41 |
600.000061
|
42 |
Executing: Spring.Echo( z )
|
42 |
Executing: Spring.Echo( z )
|
43 |
ZZZ}}}
|
43 |
ZZZ}}}
|
44 |
\n
|
44 |
\n
|
45 |
and if the command fails, I get its error message, but the widget is not getting removed.
|
45 |
and if the command fails, I get its error message, but the widget is not getting removed.
|
46 |
\n
|
46 |
\n
|
47 |
TODO/questions:
|
47 |
TODO/questions:
|
48 |
\n
|
48 |
\n
|
49 |
1. Anyone can explain how to make automatically a table of all the locals of a widget from inside a callin?
|
49 |
1. Anyone can explain how to make automatically a table of all the locals of a widget from inside a callin?
|
50 |
\n
|
50 |
\n
|
51 |
2.
I'd
like
to
make
a
dedicated
widget
serving
the
interactive
commands,
so
that
in
a
widget
I'm
working
on,
I
don't
need
to
include
the
snippet
above
(
may
be
just
'require'?)
.
Probably
need
to
replace
"sudo"
with
a
widget
name
so
the
dedicated
widget
will
know
in
which
widget
to
run
the
command.
|
51 |
2.
I'd
like
to
make
a
dedicated
widget
serving
the
interactive
commands,
so
that
in
a
widget
I'm
working
on,
I
don't
need
to
include
the
snippet
above
(
may
be
just
'require'?)
.
Probably
need
to
replace
"sudo"
with
a
widget's
name
so
the
dedicated
widget
will
know
in
which
widget
to
run
the
command.
|
52 |
\n
|
52 |
\n
|
53 |
3. I'm still interested to know where can I find the full list of callins.
|
53 |
3. I'm still interested to know where can I find the full list of callins.
|