Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ MUSHclient
➜ Tips and tricks
➜ MUSHclient variables in a lua table
MUSHclient variables in a lua table
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
Posted by
| Ircria
(24 posts) Bio
|
Date
| Sat 05 Mar 2011 11:56 AM (UTC) Amended on Sat 05 Mar 2011 11:58 AM (UTC) by Ircria
|
Message
| After seeing a friend's code for returning tables from mushclient variables while emulating var.lua, I saw an improvement in their code that could be made.
require "serialize"
function mushvartable(table)
return setmetatable({}, {
__index = function (table, key)
if not GetVariable(key) then
return nil
elseif not tonumber(GetVariable(key)) then
if string.match(GetVariable(key), "{") and string.match(GetVariable(key), "}") then
local retp = GetVariable(key)
loadstring("local ret = "..retp)()
return ret
else
return GetVariable(key)
end
else
return tonumber(key)
end
end,
__newindex = function(table, key, value)
if type(value) == 'nil' then
DeleteVariable(key)
elseif type(value) == 'table' then
SetVariable(key, serialize.save_simple(value))
else
SetVariable(key, value)
end
end,
__metatable = false
});
end
mushvars = mushvartable {}
This allows one to use the mushvars table to directly edit the MUSHclient variables without the need to type out SetVariable(), and automatically serializes and unserializes tables to and from MUSHclient variables so they can be read as if they were normal tables. (basically, the built in var.lua with table functionality). If it can be tonumber()ed, it will also return it in number form instead of a string.
Usage: Just treat it like a normal lua table.
mushvars.test = "test"
mushvars.test1 = 1
mushvars.testtab = {"whathaveyou", i = "am a named variable", {"i am in a table"}}
testvars.test = nil --deletes the variable | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #1 on Sat 05 Mar 2011 09:21 PM (UTC) Amended on Sat 05 Mar 2011 09:25 PM (UTC) by Twisol
|
Message
| If I may suggest another simplifying change:
require "serialize"
function mushvartable(table)
return setmetatable({}, {
__index = function (table, key)
local v = GetVariable(key)
if v then
local f = loadstring("return " .. GetVariable(key))
if f then
return f()
end
end
-- fallback
return v
end,
__newindex = function(table, key, value)
if value == nil then
DeleteVariable(key)
else
SetVariable(key, serialize.save_simple(value))
end
end,
__metatable = false
});
end
mushvars = mushvartable {}
It takes advantage of serialize.save_simple's ability to save primitive types as well as tables, so save_simple(1) becomes "1" and save_simple("foo") becomes (more or less) "\"foo\"". It falls back to just returning the raw variable if it couldn't be loadstring()'d. |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Nick Gammon
Australia (23,140 posts) Bio
Forum Administrator |
Date
| Reply #2 on Sat 05 Mar 2011 09:42 PM (UTC) |
Message
| Hmmm. I reworked the idea a bit. First to remove multiple calls to GetVariable, which would slow it down. Second to add a couple more features:
- Also convert "true" and "false" back to booleans
- Handle bad compile when attempting to convert tables back
- Table checks now for "{ xxx }" not just "{" and "}" somewhere in the string
- Loadstring done in a restricted environment to stop data injection like:
var = "{ hello }; os.remove 'mushclient.exe'; b = {}"
-- tvar.lua
-- ----------------------------------------------------------
-- Accessing MUSHclient variables through the 'var' table.
-- See forum thread:
-- http://www.gammon.com.au/forum/?id=4904
-- Improvements suggested:
-- http://www.gammon.com.au/forum/?id=10980
-- Date: 6th Marh 2011
--[[
* Set a variable by assigning something to it.
* Delete a variable by assigning nil to it.
* Get a variable by retrieving its value, will return nil if the variable does not exist.
Improvements in this version:
* Automatically turns things that look like numbers into numbers
* Automatically turns "true" and "false" into booleans
* Saves amd restores tables by using serialize
Examples:
require "tvar"
var.target = "kobold" -- set MUSHclient variable 'target' to kobold
print (var.target) -- print contents of MUSHclient variable
-- table
var.t1 = { "fish", "chips", "steak", { "subtable", "foo" }, drink = "beer" }
Limitations:
1. Things that look like numbers will become numbers, perhaps unintentionally
2. Things that look like tables will become tables, perhaps unintentionally
3. The words "true" and "false" will become booleans even if they were not before
4. Slightly slower than the "var" module because of the extra tests
--]]
-- ----------------------------------------------------------
require "serialize"
var = {} -- variables table
setmetatable (var,
{
-- called to access an entry
__index =
function (t, key)
local v = GetVariable (key) -- find the variable in MUSHclient variable space
-- no such variable returns nil
if v == nil then
return nil
end -- if
-- convert true and false back into booleans
if v == "true" then
return true
end -- if true
if v == "false" then
return false
end -- if false
-- if can be converted into a number return that number
local n = tonumber (v)
if n then
return n
end -- if a number
-- if variable is in the form {xxx} treat as a table
if string.sub (v, 1, 1) == "{" and string.sub (v, -1, -1) == "}" then
local t = {} -- local environment
local f = loadstring ("ret = " .. v)
-- if loadstring fails, just return as a string
if f then
setfenv (f, t) -- local environment
-- if pcall fails, just return as a string
if pcall (f) then
return t.ret
end -- if executed ok
end -- if loadstring worked
end -- if looks like a table
return v -- return "normal" variable
end, -- function __index
-- called to change or delete an entry
__newindex =
function (t, key, value)
local result -- of SetVariable
if value == nil then -- nil deletes it
result = DeleteVariable (key)
elseif type (value) == 'table' then
result = SetVariable (key, serialize.save_simple (value))
else
result = SetVariable (key, tostring (value))
end -- if
-- warn if they are using bad variable keys
if result == error_code.eInvalidObjectLabel then
error ("Bad variable key '" .. key .. "'", 2)
end -- bad variable
end -- function __newindex
}) -- end metatable
return var
I should point out that this new module is not necessarily symmetrical.
For example, something that you wanted to be a string, but happens to look like a number (eg. "5e6") now actually becomes a number (eg. 5000000).
Similarly the strings "true" and "false" become booleans, when you might not want that.
Similarly something that looks like a table now becomes one (eg. "{nick}" becomes an empty table because the variable nick is nil).
Still, if you can live with that, you may find it useful. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Nick Gammon
Australia (23,140 posts) Bio
Forum Administrator |
Date
| Reply #3 on Sat 05 Mar 2011 09:43 PM (UTC) |
Message
|
Twisol said:
If I may suggest another simplifying change:
Ninja'd while I was doing my lengthy changes. ;) |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #4 on Sat 05 Mar 2011 10:04 PM (UTC) Amended on Sat 05 Mar 2011 10:05 PM (UTC) by Twisol
|
Message
| ^_^
Nick Gammon said:*Also convert "true" and "false" back to booleans
*Handle bad compile when attempting to convert tables back
*Table checks now for "{ xxx }" not just "{" and "}" somewhere in the string
*Loadstring done in a restricted environment to stop data injection like:
var = "{ hello }; os.remove 'mushclient.exe'; b = {}"
My version handles all of these except the injection attack. Good catch!
Nick Gammon said:
1. Things that look like numbers will become numbers, perhaps unintentionally
2. Things that look like tables will become tables, perhaps unintentionally
3. The words "true" and "false" will become booleans even if they were not before
4. Slightly slower than the "var" module because of the extra tests
My version assumes that all input was sent through serialize.save_simple(), so it's always consistent. |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #5 on Sat 05 Mar 2011 10:10 PM (UTC) Amended on Sat 05 Mar 2011 10:30 PM (UTC) by Twisol
|
Message
| Merger of the two versions:
-- tvar.lua
-- ----------------------------------------------------------
-- Accessing MUSHclient variables through the 'var' table.
-- See forum thread:
-- http://www.gammon.com.au/forum/?id=4904
-- Improvements suggested:
-- http://www.gammon.com.au/forum/?id=10980
-- Date: 6th Marh 2011
--[[
* Set a variable by assigning something to it.
* Delete a variable by assigning nil to it.
* Get a variable by retrieving its value, will return nil if the variable does not exist.
Improvements in this version:
* Saves and restores primitive types and tables by using the serialize module
Examples:
require "tvar"
var.target = "kobold" -- set MUSHclient variable 'target' to kobold
print (var.target) -- print contents of MUSHclient variable
-- table
var.t1 = { "fish", "chips", "steak", { "subtable", "foo" }, drink = "beer" }
Limitations:
1. Slightly slower than the "var" module because of the extra tests
--]]
-- ----------------------------------------------------------
require "serialize"
var = {} -- variables table
setmetatable (var, {
-- called to access an entry
__index = function (t, key)
local v = GetVariable (key) -- find the variable in MUSHclient variable space
if v then
local env = {}
local f = loadstring ("return " .. v)
if f then
local ok, result = pcall (setfenv (f, env))
if ok then
return result
end
end
end
-- fallback
return v
end, -- function __index
-- called to change or delete an entry
__newindex = function (t, key, value)
local result -- of SetVariable
if value == nil then -- nil deletes it
result = DeleteVariable (key)
else
result = SetVariable (key, serialize.save_simple (value))
end -- if
-- warn if they are using bad variable keys
if result == error_code.eInvalidObjectLabel then
error ("Bad variable key '" .. key .. "'", 2)
end -- bad variable
end -- function __newindex
}) -- end metatable
return var
[EDIT]: Incorporate pcall() too. |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Nick Gammon
Australia (23,140 posts) Bio
Forum Administrator |
Date
| Reply #6 on Sun 06 Mar 2011 12:38 AM (UTC) |
Message
|
Twisol said:
My version assumes that all input was sent through serialize.save_simple(), so it's always consistent.
They are just MUSHclient variables, right? It's not like they are plugin state files over which you normally have more control.
I can imagine someone might set a variable in an alias or trigger, and then use this method to retrieve it. And who knows, someone may call their character "{}; DoCommand 'quit'; {}". I know, pretty unlikely. But if you were capturing the last chat message into a variable, perhaps a little more likely. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #7 on Sun 06 Mar 2011 03:13 AM (UTC) Amended on Sun 06 Mar 2011 03:27 AM (UTC) by Twisol
|
Message
|
Nick Gammon said:
Twisol said:
My version assumes that all input was sent through serialize.save_simple(), so it's always consistent.
They are just MUSHclient variables, right? It's not like they are plugin state files over which you normally have more control.
I can imagine someone might set a variable in an alias or trigger, and then use this method to retrieve it. And who knows, someone may call their character "{}; DoCommand 'quit'; {}". I know, pretty unlikely. But if you were capturing the last chat message into a variable, perhaps a little more likely.
Well, if you want raw variable access, you probably want the original var.lua module. It seems to me that tvar is slanted towards structured data. That said, in your example it won't deserialize anyways, because I call loadstring as loadstring("return " .. v) rather than setting a local variable. And since it errors out, it will fall back to providing the raw variable.
I may be in the minority, but to be honest I rarely use the Variable field in the editor. Normally I just use SetVariable("foo", "%1") manually because it's more explicit. In this case you could just use tvar.foo = "%1".
Errata: I should have said "assumes variables contain output from" rather than "assumes all input was sent through". |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
Posted by
| Nick Gammon
Australia (23,140 posts) Bio
Forum Administrator |
Date
| Reply #8 on Sun 06 Mar 2011 04:28 AM (UTC) |
Message
|
Twisol said:
That said, in your example it won't deserialize anyways, because I call loadstring as loadstring("return " .. v) rather than setting a local variable.
Oh well, you could do:
"{a = DoCommand 'ExitClient' }"
However your setfenv protects against that. But at least it is legal syntax:
return {a = DoCommand 'ExitClient' }
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|
Posted by
| Twisol
USA (2,257 posts) Bio
|
Date
| Reply #9 on Sun 06 Mar 2011 04:33 AM (UTC) Amended on Sun 06 Mar 2011 04:34 AM (UTC) by Twisol
|
Message
| Yep, that's true. Your version would do the same thing however. You just have to be consistent: either use tvar for getting and setting, or use the core API functions. tvar acts as an intermediary serialization layer, so it's not unreasonable to say "if you don't do this you won't get what you expect".
As a sidenote: thank you Ircria for the great contribution! |
'Soludra' on Achaea
Blog: http://jonathan.com/
GitHub: http://github.com/Twisol | Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
35,179 views.
It is now over 60 days since the last post. This thread is closed.
Refresh page
top