Loading...
  OR  Zero-K Name:    Password:   

Post edit history

Is there a way to send LuaUI commands interactively?

To display differences between versions, select one or more edits in the list using checkboxes and click "diff selected"
Post edit history
Date Editor Before After
9/9/2022 4:59:21 PMILrankrollmops before revert after revert
Before After
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.