WIP: Auios' version - Do not merge. Only here for comparison #9

Draft
Auios wants to merge 28 commits from Auios/BlockLua:master into master
16 changed files with 2824 additions and 2596 deletions
Showing only changes of commit 01f216f31e - Show all commits

2
.gitignore vendored
View File

@@ -1,3 +1 @@
.*
!.gitignore
build/ build/

View File

@@ -46,8 +46,10 @@ function filter.chain(...)
while true do while true do
if index == top then if index == top then
chunk = arg[index](chunk) chunk = arg[index](chunk)
if chunk == "" or top == n then return chunk if chunk == "" or top == n then
elseif chunk then index = index + 1 return chunk
elseif chunk then
index = index + 1
else else
top = top + 1 top = top + 1
index = top index = top
@@ -58,9 +60,14 @@ function filter.chain(...)
index = index - 1 index = index - 1
chunk = retry chunk = retry
elseif chunk then elseif chunk then
if index == n then return chunk if index == n then
else index = index + 1 end return chunk
else base.error("filter returned inappropriate nil") end else
index = index + 1
end
else
base.error("filter returned inappropriate nil")
end
end end
end end
end end
@@ -93,7 +100,9 @@ function source.file(handle, io_err)
if not chunk then handle:close() end if not chunk then handle:close() end
return chunk return chunk
end end
else return source.error(io_err or "unable to open file") end else
return source.error(io_err or "unable to open file")
end
end end
-- turns a fancy source into a simple source -- turns a fancy source into a simple source
@@ -102,8 +111,11 @@ function source.simplify(src)
return function() return function()
local chunk, err_or_new = src() local chunk, err_or_new = src()
src = err_or_new or src src = err_or_new or src
if not chunk then return nil, err_or_new if not chunk then
else return chunk end return nil, err_or_new
else
return chunk
end
end end
end end
@@ -114,10 +126,15 @@ function source.string(s)
return function() return function()
local chunk = string.sub(s, i, i + BLOCKSIZE - 1) local chunk = string.sub(s, i, i + BLOCKSIZE - 1)
i = i + BLOCKSIZE i = i + BLOCKSIZE
if chunk ~= "" then return chunk if chunk ~= "" then
else return nil end return chunk
else
return nil
end
end
else
return source.empty()
end end
else return source.empty() end
end end
-- creates rewindable source -- creates rewindable source
@@ -127,8 +144,11 @@ function source.rewind(src)
return function(chunk) return function(chunk)
if not chunk then if not chunk then
chunk = table.remove(t) chunk = table.remove(t)
if not chunk then return src() if not chunk then
else return chunk end return src()
else
return chunk
end
else else
table.insert(t, chunk) table.insert(t, chunk)
end end
@@ -228,9 +248,13 @@ function sink.file(handle, io_err)
if not chunk then if not chunk then
handle:close() handle:close()
return 1 return 1
else return handle:write(chunk) end else
return handle:write(chunk)
end
end
else
return sink.error(io_err or "unable to open file")
end end
else return sink.error(io_err or "unable to open file") end
end end
-- creates a sink that discards data -- creates a sink that discards data
@@ -262,7 +286,9 @@ function sink.chain(f, snk)
if filtered == done then return 1 end if filtered == done then return 1 end
filtered = f(done) filtered = f(done)
end end
else return 1 end else
return 1
end
end end
end end
@@ -273,8 +299,11 @@ end
function pump.step(src, snk) function pump.step(src, snk)
local chunk, src_err = src() local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err) local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1 if chunk and ret then
else return nil, src_err or snk_err end return 1
else
return nil, src_err or snk_err
end
end end
-- pumps all data from a source to a sink, using a step function -- pumps all data from a source to a sink, using a step function
@@ -284,9 +313,11 @@ function pump.all(src, snk, step)
while true do while true do
local ret, err = step(src, snk) local ret, err = step(src, snk)
if not ret then if not ret then
if err then return nil, err if err then
else return 1 end return nil, err
else
return 1
end
end end
end end
end end

View File

@@ -29,7 +29,9 @@ local function choose(table)
local f = table[name or "nil"] local f = table[name or "nil"]
if not f then if not f then
base.error("unknown key (" .. base.tostring(name) .. ")", 3) base.error("unknown key (" .. base.tostring(name) .. ")", 3)
else return f(opt1, opt2) end else
return f(opt1, opt2)
end
end end
end end
@@ -54,9 +56,14 @@ end
local function format(chunk) local function format(chunk)
if chunk then if chunk then
if chunk == "" then return "''" if chunk == "" then
else return string.len(chunk) end return "''"
else return "nil" end else
return string.len(chunk)
end
else
return "nil"
end
end end
-- define the line-wrap filters -- define the line-wrap filters

View File

@@ -47,8 +47,11 @@ function choose(table)
name, opt1, opt2 = "default", name, opt1 name, opt1, opt2 = "default", name, opt1
end end
local f = table[name or "nil"] local f = table[name or "nil"]
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) if not f then
else return f(opt1, opt2) end base.error("unknown key (" .. base.tostring(name) .. ")", 3)
else
return f(opt1, opt2)
end
end end
end end
@@ -70,7 +73,9 @@ sinkt["close-when-done"] = function(sock)
if not chunk then if not chunk then
sock:close() sock:close()
return 1 return 1
else return sock:send(chunk) end else
return sock:send(chunk)
end
end end
}) })
end end
@@ -81,8 +86,11 @@ sinkt["keep-open"] = function(sock)
dirty = function() return sock:dirty() end dirty = function() return sock:dirty() end
}, { }, {
__call = function(self, chunk, err) __call = function(self, chunk, err)
if chunk then return sock:send(chunk) if chunk then
else return 1 end return sock:send(chunk)
else
return 1
end
end end
}) })
end end
@@ -116,12 +124,15 @@ sourcet["until-closed"] = function(sock)
__call = function() __call = function()
if done then return nil end if done then return nil end
local chunk, err, partial = sock:receive(socket.BLOCKSIZE) local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
if not err then return chunk if not err then
return chunk
elseif err == "closed" then elseif err == "closed" then
sock:close() sock:close()
done = 1 done = 1
return partial return partial
else return nil, err end else
return nil, err
end
end end
}) })
end end
@@ -130,4 +141,3 @@ end
sourcet["default"] = sourcet["until-closed"] sourcet["default"] = sourcet["until-closed"]
source = choose(sourcet) source = choose(sourcet)

View File

@@ -194,7 +194,9 @@ local function override(t)
u[i] = v u[i] = v
end end
return u return u
else return t end else
return t
end
end end
local function tput(putt) local function tput(putt)
@@ -236,8 +238,11 @@ local function sput(u, body)
end end
put = socket.protect(function(putt, body) put = socket.protect(function(putt, body)
if base.type(putt) == "string" then return sput(putt, body) if base.type(putt) == "string" then
else return tput(putt) end return sput(putt, body)
else
return tput(putt)
end
end) end)
local function tget(gett) local function tget(gett)
@@ -275,7 +280,9 @@ command = socket.protect(function(cmdt)
end) end)
get = socket.protect(function(gett) get = socket.protect(function(gett)
if base.type(gett) == "string" then return sget(gett) if base.type(gett) == "string" then
else return tget(gett) end return sget(gett)
else
return tget(gett)
end
end) end)

View File

@@ -52,8 +52,11 @@ local function receiveheaders(sock, headers)
if err then return nil, err end if err then return nil, err end
end end
-- save pair in table -- save pair in table
if headers[name] then headers[name] = headers[name] .. ", " .. value if headers[name] then
else headers[name] = value end headers[name] = headers[name] .. ", " .. value
else
headers[name] = value
end
end end
return headers return headers
end end
@@ -162,8 +165,11 @@ function metat.__index:receivebody(headers, sink, step)
local length = base.tonumber(headers["content-length"]) local length = base.tonumber(headers["content-length"])
local t = headers["transfer-encoding"] -- shortcut local t = headers["transfer-encoding"] -- shortcut
local mode = "default" -- connection close local mode = "default" -- connection close
if t and t ~= "identity" then mode = "http-chunked" if t and t ~= "identity" then
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end mode = "http-chunked"
elseif base.tonumber(headers["content-length"]) then
mode = "by-length"
end
return self.try(ltn12.pump.all(socket.source(mode, self.c, length), return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
sink, step)) sink, step))
end end
@@ -345,6 +351,9 @@ local function srequest(u, b)
end end
request = socket.protect(function(reqt, body) request = socket.protect(function(reqt, body)
if base.type(reqt) == "string" then return srequest(reqt, body) if base.type(reqt) == "string" then
else return trequest(reqt) end return srequest(reqt, body)
else
return trequest(reqt)
end
end) end)

View File

@@ -192,9 +192,13 @@ local function send_source(mesgt)
-- send body from source -- send body from source
while true do while true do
local chunk, err = mesgt.body() local chunk, err = mesgt.body()
if err then coroutine.yield(nil, err) if err then
elseif chunk then coroutine.yield(chunk) coroutine.yield(nil, err)
else break end elseif chunk then
coroutine.yield(chunk)
else
break
end
end end
end end
@@ -211,9 +215,13 @@ end
-- message source -- message source
function send_message(mesgt) function send_message(mesgt)
if base.type(mesgt.body) == "table" then send_multipart(mesgt) if base.type(mesgt.body) == "table" then
elseif base.type(mesgt.body) == "function" then send_source(mesgt) send_multipart(mesgt)
else send_string(mesgt) end elseif base.type(mesgt.body) == "function" then
send_source(mesgt)
else
send_string(mesgt)
end
end end
-- set defaul headers -- set defaul headers
@@ -233,8 +241,11 @@ function message(mesgt)
local co = coroutine.create(function() send_message(mesgt) end) local co = coroutine.create(function() send_message(mesgt) end)
return function() return function()
local ret, a, b = coroutine.resume(co) local ret, a, b = coroutine.resume(co)
if ret then return a, b if ret then
else return nil, a end return a, b
else
return nil, a
end
end end
end end

View File

@@ -57,10 +57,15 @@ function metat.__index:check(ok)
end end
return nil, reply return nil, reply
else else
if string.find(code, ok) then return base.tonumber(code), reply if string.find(code, ok) then
else return nil, reply end return base.tonumber(code), reply
else
return nil, reply
end
end
else
return ok(base.tonumber(code), reply)
end end
else return ok(base.tonumber(code), reply) end
end end
function metat.__index:command(cmd, arg) function metat.__index:command(cmd, arg)
@@ -120,4 +125,3 @@ function connect(host, port, timeout, create)
end end
return base.setmetatable({ c = c }, metat) return base.setmetatable({ c = c }, metat)
end end

View File

@@ -56,8 +56,11 @@ local segment_set = make_set {
local function protect_segment(s) local function protect_segment(s)
return string.gsub(s, "([^A-Za-z0-9_])", function(c) return string.gsub(s, "([^A-Za-z0-9_])", function(c)
if segment_set[c] then return c if segment_set[c] then
else return string.format("%%%02x", string.byte(c)) end return c
else
return string.format("%%%02x", string.byte(c))
end
end) end)
end end
@@ -136,7 +139,9 @@ function parse(url, default)
end) end)
-- get scheme -- get scheme
url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
function(s) parsed.scheme = s; return "" end) function(s)
parsed.scheme = s; return ""
end)
-- get authority -- get authority
url = string.gsub(url, "^//([^/]*)", function(n) url = string.gsub(url, "^//([^/]*)", function(n)
parsed.authority = n parsed.authority = n
@@ -157,14 +162,20 @@ function parse(url, default)
local authority = parsed.authority local authority = parsed.authority
if not authority then return parsed end if not authority then return parsed end
authority = string.gsub(authority, "^([^@]*)@", authority = string.gsub(authority, "^([^@]*)@",
function(u) parsed.userinfo = u; return "" end) function(u)
parsed.userinfo = u; return ""
end)
authority = string.gsub(authority, ":([^:]*)$", authority = string.gsub(authority, ":([^:]*)$",
function(p) parsed.port = p; return "" end) function(p)
parsed.port = p; return ""
end)
if authority ~= "" then parsed.host = authority end if authority ~= "" then parsed.host = authority end
local userinfo = parsed.userinfo local userinfo = parsed.userinfo
if not userinfo then return parsed end if not userinfo then return parsed end
userinfo = string.gsub(userinfo, ":([^:]*)$", userinfo = string.gsub(userinfo, ":([^:]*)$",
function(p) parsed.password = p; return "" end) function(p)
parsed.password = p; return ""
end)
parsed.user = userinfo parsed.user = userinfo
return parsed return parsed
end end
@@ -218,9 +229,12 @@ function absolute(base_url, relative_url)
base_parsed = parse(base_url) base_parsed = parse(base_url)
end end
local relative_parsed = parse(relative_url) local relative_parsed = parse(relative_url)
if not base_parsed then return relative_url if not base_parsed then
elseif not relative_parsed then return base_url return relative_url
elseif relative_parsed.scheme then return relative_url elseif not relative_parsed then
return base_url
elseif relative_parsed.scheme then
return relative_url
else else
relative_parsed.scheme = base_parsed.scheme relative_parsed.scheme = base_parsed.scheme
if not relative_parsed.authority then if not relative_parsed.authority then

View File

@@ -4,7 +4,9 @@
-- Utility: Convert a list of strings into a map of string->true -- Utility: Convert a list of strings into a map of string->true
local function tmap(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end local function tmap(t)
local u = {}; for _, n in ipairs(t) do u[n] = true end; return u;
end
-- Save banned global variables for wrapping with safe functions -- Save banned global variables for wrapping with safe functions
local old_io = io local old_io = io
@@ -104,6 +106,7 @@ function _bllua_io_open(fn, md)
return nil, err return nil, err
end end
end end
-- Allow io.type (works on file handles returned by io.open) -- Allow io.type (works on file handles returned by io.open)
function _bllua_io_type(f) function _bllua_io_type(f)
return old_io.type(f) return old_io.type(f)
@@ -128,6 +131,7 @@ function _bllua_requiresecure(name)
return old_require(name) return old_require(name)
end end
end end
package = { package = {
seeall = old_package.seeall, seeall = old_package.seeall,
} }

View File

@@ -118,7 +118,8 @@ end
bl._forceType = bl._forceType or {} bl._forceType = bl._forceType or {}
local function valFromTs(val, name, name2) -- todo: ensure name and name2 are already lowercase local function valFromTs(val, name, name2) -- todo: ensure name and name2 are already lowercase
if type(val) ~= 'string' then if type(val) ~= 'string' then
error('valFromTs: expected string, got '..type(val), 3) end error('valFromTs: expected string, got ' .. type(val), 3)
end
if name then if name then
name = name:lower() name = name:lower()
if bl._forceType[name] then if bl._forceType[name] then
@@ -143,9 +144,11 @@ local function valFromTs(val, name, name2) -- todo: ensure name and name2 are al
'^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) ' .. '^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) ' ..
'(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') '(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$')
-- box (2 vectors) -- box (2 vectors)
if x1S then return { if x1S then
return {
vector { tonumber(x1S), tonumber(y1S), tonumber(z1S) }, vector { tonumber(x1S), tonumber(y1S), tonumber(z1S) },
vector{tonumber(x2S),tonumber(y2S),tonumber(z2S)} } end vector { tonumber(x2S), tonumber(y2S), tonumber(z2S) } }
end
-- string -- string
return val return val
end end
@@ -162,15 +165,18 @@ end
local function classFromForceTypeStr(name) local function classFromForceTypeStr(name)
local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$') local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$')
if not class then if not class then
class, rest = name:match('^([a-zA-Z0-9_]+)(%..+)$') end class, rest = name:match('^([a-zA-Z0-9_]+)(%..+)$')
end
return class, rest return class, rest
end end
local setForceType local setForceType
setForceType = function(ftname, typ) setForceType = function(ftname, typ)
if typ ~= 'boolean' and typ ~= 'object' and typ ~= nil then if typ ~= 'boolean' and typ ~= 'object' and typ ~= nil then
error('bl.type: can only set type to \'boolean\', \'object\', or nil', 2) end error('bl.type: can only set type to \'boolean\', \'object\', or nil', 2)
end
if not isValidFuncNameNsArgn(ftname) then if not isValidFuncNameNsArgn(ftname) then
error('bl.type: invalid function or variable name \''..ftname..'\'', 2) end error('bl.type: invalid function or variable name \'' .. ftname .. '\'', 2)
end
ftname = ftname:lower() ftname = ftname:lower()
@@ -201,8 +207,11 @@ local function tsIsFunction(name) return tsBool(_bllua_ts.call('isFunction', nam
local function tsIsFunctionNs(ns, name) return tsBool(_bllua_ts.call('isFunction', ns, name)) end local function tsIsFunctionNs(ns, name) return tsBool(_bllua_ts.call('isFunction', ns, name)) end
local function tsIsFunctionNsname(nsname) local function tsIsFunctionNsname(nsname)
local ns, name = nsname:match('^([^:]+)::([^:]+)$') local ns, name = nsname:match('^([^:]+)::([^:]+)$')
if ns then return tsIsFunctionNs(ns, name) if ns then
else return tsIsFunction(nsname) end return tsIsFunctionNs(ns, name)
else
return tsIsFunction(nsname)
end
end end
function bl.isObject(obj) function bl.isObject(obj)
@@ -215,12 +224,15 @@ function bl.isObject(obj)
end end
return tsIsObject(obj) return tsIsObject(obj)
end end
function bl.isFunction(a1, a2) function bl.isFunction(a1, a2)
if type(a1) ~= 'string' then if type(a1) ~= 'string' then
error('bl.isFunction: argument #1: expected string', 2) end error('bl.isFunction: argument #1: expected string', 2)
end
if a2 then if a2 then
if type(a2) ~= 'string' then if type(a2) ~= 'string' then
error('bl.isFunction: argument #2: expected string', 2) end error('bl.isFunction: argument #2: expected string', 2)
end
return tsIsFunctionNs(a1, a2) return tsIsFunctionNs(a1, a2)
else else
return tsIsFunction(a1) return tsIsFunction(a1)
@@ -237,9 +249,11 @@ local tsClassMeta = {
bl._objectUserMetas = bl._objectUserMetas or {} bl._objectUserMetas = bl._objectUserMetas or {}
function bl.class(cname, inhname) function bl.class(cname, inhname)
if not (type(cname) == 'string' and isValidFuncName(cname)) then if not (type(cname) == 'string' and isValidFuncName(cname)) then
error('bl.class: argument #1: invalid class name', 2) end error('bl.class: argument #1: invalid class name', 2)
end
if not (inhname == nil or (type(inhname) == 'string' and isValidFuncName(inhname))) then if not (inhname == nil or (type(inhname) == 'string' and isValidFuncName(inhname))) then
error('bl.class: argument #2: inherit name must be a string or nil', 2) end error('bl.class: argument #2: inherit name must be a string or nil', 2)
end
cname = cname:lower() cname = cname:lower()
local met = bl._objectUserMetas[cname] or { local met = bl._objectUserMetas[cname] or {
@@ -254,15 +268,18 @@ function bl.class(cname, inhname)
inhname = inhname:lower() inhname = inhname:lower()
local inh = bl._objectUserMetas[inhname] local inh = bl._objectUserMetas[inhname]
if not inh then error('bl.class: argument #2: \''..inhname..'\' is not the '.. if not inh then
'name of an existing class', 2) end error('bl.class: argument #2: \'' .. inhname .. '\' is not the ' ..
'name of an existing class', 2)
end
inh._children[cname] = true inh._children[cname] = true
local inhI = met._inherit local inhI = met._inherit
if inhI and inhI ~= inh then if inhI and inhI ~= inh then
error('bl.class: argument #2: class already exists and ' .. error('bl.class: argument #2: class already exists and ' ..
'inherits a different parent.', 2) end 'inherits a different parent.', 2)
end
met._inherit = inh met._inherit = inh
-- apply inherited method and field types -- apply inherited method and field types
@@ -274,6 +291,7 @@ function bl.class(cname, inhname)
end end
end end
end end
local function objectInheritedMetas(name) local function objectInheritedMetas(name)
local inh = bl._objectUserMetas[name:lower()] local inh = bl._objectUserMetas[name:lower()]
return function() return function()
@@ -288,15 +306,18 @@ local tsObjectMeta = {
-- Return torque member function or value -- Return torque member function or value
__index = function(t, name) __index = function(t, name)
if rawget(t, '_deleted') then if rawget(t, '_deleted') then
error('ts object index: object no longer exists', 2) end error('ts object index: object no longer exists', 2)
end
if type(name) ~= 'string' and type(name) ~= 'number' then if type(name) ~= 'string' and type(name) ~= 'number' then
error('ts object index: index must be a string or number', 2) end error('ts object index: index must be a string or number', 2)
end
if getmetatable(t)[name] then if getmetatable(t)[name] then
return getmetatable(t)[name] return getmetatable(t)[name]
elseif type(name) == 'number' then elseif type(name) == 'number' then
if not tsIsFunctionNs(rawget(t, '_tsNamespace'), 'getObject') then if not tsIsFunctionNs(rawget(t, '_tsNamespace'), 'getObject') then
error('ts object __index: index is number, but object does not have ' .. error('ts object __index: index is number, but object does not have ' ..
'getObject method', 2) end 'getObject method', 2)
end
return toTsObject(_bllua_ts.callobj(t._tsObjectId, 'getObject', return toTsObject(_bllua_ts.callobj(t._tsObjectId, 'getObject',
tostring(name))) tostring(name)))
else else
@@ -325,9 +346,11 @@ local tsObjectMeta = {
-- Use :set() to set Torque data -- Use :set() to set Torque data
__newindex = function(t, name, val) __newindex = function(t, name, val)
if rawget(t, '_deleted') then if rawget(t, '_deleted') then
error('ts object newindex: object no longer exists', 2) end error('ts object newindex: object no longer exists', 2)
end
if type(name) ~= 'string' then if type(name) ~= 'string' then
error('ts object newindex: index must be a string', 2) end error('ts object newindex: index must be a string', 2)
end
rawset(t, name, val) rawset(t, name, val)
-- create strong reference since it's now storing lua data -- create strong reference since it's now storing lua data
bl._objectsStrong[rawget(t, '_tsObjectId')] = t bl._objectsStrong[rawget(t, '_tsObjectId')] = t
@@ -336,11 +359,14 @@ local tsObjectMeta = {
-- Use to set torque data -- Use to set torque data
set = function(t, name, val) set = function(t, name, val)
if t == nil or type(t) ~= 'table' or not t._tsObjectId then if t == nil or type(t) ~= 'table' or not t._tsObjectId then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
error('ts object method: object no longer exists', 2) end error('ts object method: object no longer exists', 2)
end
if type(name) ~= 'string' then if type(name) ~= 'string' then
error('ts object :set(): index must be a string', 2) end error('ts object :set(): index must be a string', 2)
end
_bllua_ts.setfield(t._tsObjectId, name, valToTs(val)) _bllua_ts.setfield(t._tsObjectId, name, valToTs(val))
end, end,
-- __tostring: Called when printing -- __tostring: Called when printing
@@ -353,9 +379,11 @@ local tsObjectMeta = {
-- If the object has a getCount method, return its count -- If the object has a getCount method, return its count
__len = function(t) __len = function(t)
if t._deleted then if t._deleted then
error('ts object __len: object no longer exists', 2) end error('ts object __len: object no longer exists', 2)
end
if not tsIsFunctionNs(t._tsNamespace, 'getCount') then if not tsIsFunctionNs(t._tsNamespace, 'getCount') then
error('ts object __len: object has no getCount method', 2) end error('ts object __len: object has no getCount method', 2)
end
return tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount')) return tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount'))
end, end,
-- object:members() -- object:members()
@@ -363,14 +391,17 @@ local tsObjectMeta = {
-- for index, object in group:members() do ... end -- for index, object in group:members() do ... end
members = function(t) members = function(t)
if t == nil then if t == nil then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
error('ts object method: object no longer exists', 2) end error('ts object method: object no longer exists', 2)
end
if not ( if not (
tsIsFunctionNs(t._tsNamespace, 'getCount') and tsIsFunctionNs(t._tsNamespace, 'getCount') and
tsIsFunctionNs(t._tsNamespace, 'getObject')) then tsIsFunctionNs(t._tsNamespace, 'getObject')) then
error('ts object :members() - ' .. error('ts object :members() - ' ..
'Object does not have getCount and getObject methods', 2) end 'Object does not have getCount and getObject methods', 2)
end
local count = tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount')) local count = tonumber(_bllua_ts.callobj(t._tsObjectId, 'getCount'))
local idx = 0 local idx = 0
return function() return function()
@@ -387,23 +418,29 @@ local tsObjectMeta = {
-- Wrap some Torque functions for performance and error checking -- Wrap some Torque functions for performance and error checking
getName = function(t) getName = function(t)
if t == nil then if t == nil then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
error('ts object method: object no longer exists', 2) end error('ts object method: object no longer exists', 2)
end
return t._tsName return t._tsName
end, end,
getId = function(t) getId = function(t)
if t == nil then if t == nil then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
error('ts object method: object no longer exists', 2) end error('ts object method: object no longer exists', 2)
end
return tonumber(t._tsObjectId) return tonumber(t._tsObjectId)
end, end,
getType = function(t) getType = function(t)
if t == nil then if t == nil then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
error('ts object method: object no longer exists', 2) end error('ts object method: object no longer exists', 2)
end
return tsTypesByNum[_bllua_ts.callobj(t._tsObjectId, 'getType')] return tsTypesByNum[_bllua_ts.callobj(t._tsObjectId, 'getType')]
end, end,
---- Schedule method for objects ---- Schedule method for objects
@@ -425,9 +462,11 @@ local tsObjectMeta = {
--end, --end,
exists = function(t) exists = function(t)
if t == nil then if t == nil then
error('ts object method: be sure to use :func() not .func()', 2) end error('ts object method: be sure to use :func() not .func()', 2)
end
if t._deleted then if t._deleted then
return false end return false
end
return tsIsObject(t._tsObjectId) return tsIsObject(t._tsObjectId)
end, end,
} }
@@ -457,13 +496,15 @@ end
-- Return a Torque object for the object ID string, or create one if none exists -- Return a Torque object for the object ID string, or create one if none exists
toTsObject = function(idiS) toTsObject = function(idiS)
if type(idiS) ~= 'string' then if type(idiS) ~= 'string' then
error('toTsObject: input must be a string', 2) end error('toTsObject: input must be a string', 2)
end
idiS = idiS:lower() idiS = idiS:lower()
if bl._objectsWeak[idiS] then return bl._objectsWeak[idiS] end if bl._objectsWeak[idiS] then return bl._objectsWeak[idiS] end
if not tsBool(_bllua_ts.call('isObject', idiS)) then if not tsBool(_bllua_ts.call('isObject', idiS)) then
--error('toTsObject: object \''..idiS..'\' does not exist', 2) end --error('toTsObject: object \''..idiS..'\' does not exist', 2) end
return nil end return nil
end
local className = _bllua_ts.callobj(idiS, 'getClassName') local className = _bllua_ts.callobj(idiS, 'getClassName')
local obj = { local obj = {
@@ -530,12 +571,15 @@ function bl.call(func, ...)
local argsS = arglistToTs(args) local argsS = arglistToTs(args)
return _bllua_ts.call(func, unpack(argsS)) return _bllua_ts.call(func, unpack(argsS))
end end
function bl.eval(code) function bl.eval(code)
return valFromTs(_bllua_ts.eval(code)) return valFromTs(_bllua_ts.eval(code))
end end
function bl.exec(file) function bl.exec(file)
return valFromTs(_bllua_ts.call('exec', file)) return valFromTs(_bllua_ts.call('exec', file))
end end
function bl.boolean(val) function bl.boolean(val)
return val ~= nil and return val ~= nil and
val ~= false and val ~= false and
@@ -543,6 +587,7 @@ function bl.boolean(val)
--val~='0' and --val~='0' and
val ~= 0 val ~= 0
end end
function bl.object(id) function bl.object(id)
if type(id) == 'table' and id._tsObjectId then if type(id) == 'table' and id._tsObjectId then
return id return id
@@ -552,14 +597,17 @@ function bl.object(id)
error('bl.toobject: id must be a ts object, number, or string', 2) error('bl.toobject: id must be a ts object, number, or string', 2)
end end
end end
function bl.array(name, ...) function bl.array(name, ...)
local rest = { ... } local rest = { ... }
return name .. table.concat(rest, '_') return name .. table.concat(rest, '_')
end end
function _bllua_call(fnameS, ...) function _bllua_call(fnameS, ...)
local args = arglistFromTs(fnameS:lower(), { ... }) local args = arglistFromTs(fnameS:lower(), { ... })
if not _G[fnameS] then if not _G[fnameS] then
error('luacall: no global lua function named \''..fnameS..'\'') end error('luacall: no global lua function named \'' .. fnameS .. '\'')
end
-- todo: library fields and object methods -- todo: library fields and object methods
local res = _G[fnameS](args) local res = _G[fnameS](args)
return valToTs(res) return valToTs(res)
@@ -591,6 +639,7 @@ function bl.schedule(time, cb, ...)
bl._scheduleTable[id] = sch bl._scheduleTable[id] = sch
return sch return sch
end end
function _bllua_schedule_callback(idS) function _bllua_schedule_callback(idS)
local id = tonumber(idS) or local id = tonumber(idS) or
error('_bllua_schedule_callback: invalid id: ' .. tostring(idS)) error('_bllua_schedule_callback: invalid id: ' .. tostring(idS))
@@ -609,9 +658,11 @@ function _bllua_process_cmd(cmdS, ...)
if not func then error('_bllua_process_cmd: no cmd named \'' .. cmd .. '\'') end if not func then error('_bllua_process_cmd: no cmd named \'' .. cmd .. '\'') end
func(unpack(args)) --pcall(func, unpack(args)) func(unpack(args)) --pcall(func, unpack(args))
end end
local function addCmd(cmd, func) local function addCmd(cmd, func)
if not isValidFuncName(cmd) then if not isValidFuncName(cmd) then
error('addCmd: invalid function name \''..tostring(cmd)..'\'') end error('addCmd: invalid function name \'' .. tostring(cmd) .. '\'')
end
bl._cmds[cmd] = func bl._cmds[cmd] = func
local arglist = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p' local arglist = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p'
_bllua_ts.eval('function ' .. cmd .. '(' .. arglist .. '){' .. _bllua_ts.eval('function ' .. cmd .. '(' .. arglist .. '){' ..
@@ -622,6 +673,7 @@ function bl.addServerCmd(name, func)
addCmd('servercmd' .. name, func) addCmd('servercmd' .. name, func)
bl._forceType['servercmd' .. name .. ':1'] = 'object' bl._forceType['servercmd' .. name .. ':1'] = 'object'
end end
function bl.addClientCmd(name, func) function bl.addClientCmd(name, func)
name = name:lower() name = name:lower()
addCmd('clientcmd' .. name, func) addCmd('clientcmd' .. name, func)
@@ -633,11 +685,13 @@ function bl.commandToServer(cmd, ...)
_bllua_ts.call('addTaggedString', cmd), _bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({ ... }))) unpack(arglistToTs({ ... })))
end end
function bl.commandToClient(cmd, ...) function bl.commandToClient(cmd, ...)
_bllua_ts.call('commandToClient', _bllua_ts.call('commandToClient',
_bllua_ts.call('addTaggedString', cmd), _bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({ ... }))) unpack(arglistToTs({ ... })))
end end
function bl.commandToAll(cmd, ...) function bl.commandToAll(cmd, ...)
_bllua_ts.call('commandToAll', _bllua_ts.call('commandToAll',
_bllua_ts.call('addTaggedString', cmd), _bllua_ts.call('addTaggedString', cmd),
@@ -665,14 +719,16 @@ local function deactivatePackage(pkg)
end end
local hookNargs = 8 local hookNargs = 8
local hookArglistLocal = '%a,%b,%c,%d,%e,%f,%g,%h' local hookArglistLocal = '%a,%b,%c,%d,%e,%f,%g,%h'
local hookArglistGlobal = '$_bllua_hook_arg1,$_bllua_hook_arg2,$_bllua_hook_arg3,$_bllua_hook_arg4,$_bllua_hook_arg5,$_bllua_hook_arg6,$_bllua_hook_arg7,$_bllua_hook_arg8' local hookArglistGlobal =
'$_bllua_hook_arg1,$_bllua_hook_arg2,$_bllua_hook_arg3,$_bllua_hook_arg4,$_bllua_hook_arg5,$_bllua_hook_arg6,$_bllua_hook_arg7,$_bllua_hook_arg8'
bl._hooks = bl._hooks or {} bl._hooks = bl._hooks or {}
function _bllua_process_hook_before(pkgS, nameS, ...) function _bllua_process_hook_before(pkgS, nameS, ...)
local args = arglistFromTs(nameS, { ... }) local args = arglistFromTs(nameS, { ... })
local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and
bl._hooks[pkgS][nameS].before bl._hooks[pkgS][nameS].before
if not func then if not func then
error('_bllua_process_hook_before: no hook for '..pkgs..':'..nameS) end error('_bllua_process_hook_before: no hook for ' .. pkgs .. ':' .. nameS)
end
_bllua_ts.setvar('_bllua_hook_abort', '0') _bllua_ts.setvar('_bllua_hook_abort', '0')
func(args) --pcall(func, args) func(args) --pcall(func, args)
if args._return then if args._return then
@@ -683,6 +739,7 @@ function _bllua_process_hook_before(pkgS, nameS, ...)
_bllua_ts.setvar('_bllua_hook_arg' .. i, valToTs(args[i])) _bllua_ts.setvar('_bllua_hook_arg' .. i, valToTs(args[i]))
end end
end end
function _bllua_process_hook_after(pkgS, nameS, resultS, ...) function _bllua_process_hook_after(pkgS, nameS, resultS, ...)
nameS = nameS:lower() nameS = nameS:lower()
local args = arglistFromTs(nameS, { ... }) local args = arglistFromTs(nameS, { ... })
@@ -690,10 +747,12 @@ function _bllua_process_hook_after(pkgS, nameS, resultS, ...)
local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and
bl._hooks[pkgS][nameS].after bl._hooks[pkgS][nameS].after
if not func then if not func then
error('_bllua_process_hook_after: no hook for '..pkgs..':'..nameS) end error('_bllua_process_hook_after: no hook for ' .. pkgs .. ':' .. nameS)
end
func(args) --pcall(func, args) func(args) --pcall(func, args)
return valToTs(args._return) return valToTs(args._return)
end end
local function updateHook(pkg, name, hk) local function updateHook(pkg, name, hk)
local beforeCode = hk.before and local beforeCode = hk.before and
('_bllua_luacall("_bllua_process_hook_before", "' .. pkg .. '","' .. name .. ('_bllua_luacall("_bllua_process_hook_before", "' .. pkg .. '","' .. name ..
@@ -719,13 +778,17 @@ local function updateHook(pkg, name, hk)
end end
function bl.hook(pkg, name, time, func) function bl.hook(pkg, name, time, func)
if not isValidFuncName(pkg) then if not isValidFuncName(pkg) then
error('bl.hook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end error('bl.hook: argument #1: invalid package name \'' .. tostring(pkg) .. '\'', 2)
end
if not isValidFuncNameNs(name) then if not isValidFuncNameNs(name) then
error('bl.hook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end error('bl.hook: argument #2: invalid function name \'' .. tostring(name) .. '\'', 2)
end
if time ~= 'before' and time ~= 'after' then if time ~= 'before' and time ~= 'after' then
error('bl.hook: argument #3: time must be \'before\' or \'after\'', 2) end error('bl.hook: argument #3: time must be \'before\' or \'after\'', 2)
end
if type(func) ~= 'function' then if type(func) ~= 'function' then
error('bl.hook: argument #4: expected a function', 2) end error('bl.hook: argument #4: expected a function', 2)
end
bl._hooks[pkg] = bl._hooks[pkg] or {} bl._hooks[pkg] = bl._hooks[pkg] or {}
bl._hooks[pkg][name] = bl._hooks[pkg][name] or {} bl._hooks[pkg][name] = bl._hooks[pkg][name] or {}
@@ -734,16 +797,20 @@ function bl.hook(pkg, name, time, func)
updateHook(pkg, name, bl._hooks[pkg][name]) updateHook(pkg, name, bl._hooks[pkg][name])
activatePackage(pkg) activatePackage(pkg)
end end
local function tableEmpty(t) local function tableEmpty(t)
return next(t) ~= nil return next(t) ~= nil
end end
function bl.unhook(pkg, name, time) function bl.unhook(pkg, name, time)
if not isValidFuncName(pkg) then if not isValidFuncName(pkg) then
error('bl.unhook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end error('bl.unhook: argument #1: invalid package name \'' .. tostring(pkg) .. '\'', 2)
end
if not isValidFuncNameNs(name) then if not isValidFuncNameNs(name) then
error('bl.unhook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end error('bl.unhook: argument #2: invalid function name \'' .. tostring(name) .. '\'', 2)
end
if time ~= 'before' and time ~= 'after' then if time ~= 'before' and time ~= 'after' then
error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2) end error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2)
end
if not name then if not name then
if bl._hooks[pkg] then if bl._hooks[pkg] then
@@ -767,7 +834,8 @@ function bl.unhook(pkg, name, time)
updateHook(pkg, name, {}) updateHook(pkg, name, {})
else else
if time ~= 'before' and time ~= 'after' then if time ~= 'before' and time ~= 'after' then
error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2) end error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2)
end
bl._hooks[pkg][name][time] = nil bl._hooks[pkg][name][time] = nil
if tableEmpty(bl._hooks[pkg][name]) and tableEmpty(bl._hooks[pkg]) then if tableEmpty(bl._hooks[pkg][name]) and tableEmpty(bl._hooks[pkg]) then
bl._hooks[pkg] = nil bl._hooks[pkg] = nil
@@ -787,14 +855,16 @@ end
-- Container search/raycast -- Container search/raycast
local function vecToTs(v) local function vecToTs(v)
if not isTsVector(v) then if not isTsVector(v) then
error('vecToTs: argument is not a vector', 3) end error('vecToTs: argument is not a vector', 3)
end
return table.concat(v, ' ') return table.concat(v, ' ')
end end
local function maskToTs(mask) local function maskToTs(mask)
if type(mask) == 'string' then if type(mask) == 'string' then
local val = tsTypesByName[mask:lower()] local val = tsTypesByName[mask:lower()]
if not val then if not val then
error('maskToTs: invalid mask \''..mask..'\'', 3) end error('maskToTs: invalid mask \'' .. mask .. '\'', 3)
end
return tostring(val) return tostring(val)
elseif type(mask) == 'table' then elseif type(mask) == 'table' then
local tval = 0 local tval = 0
@@ -804,7 +874,8 @@ local function maskToTs(mask)
local val = tsTypesByName[v:lower()] local val = tsTypesByName[v:lower()]
if not val then if not val then
error('maskToTs: invalid mask \'' .. v .. error('maskToTs: invalid mask \'' .. v ..
'\' at index '..i..' in mask list', 3) end '\' at index ' .. i .. ' in mask list', 3)
end
tval = tval + val tval = tval + val
seen[v] = true seen[v] = true
end end
@@ -846,6 +917,7 @@ function bl.raycast(start, stop, mask, ignores)
return hit, pos, norm return hit, pos, norm
end end
end end
local function tsContainerSearchIterator() local function tsContainerSearchIterator()
local retS = _bllua_ts.call('containerSearchNext') local retS = _bllua_ts.call('containerSearchNext')
if retS == '0' then if retS == '0' then
@@ -862,10 +934,12 @@ function bl.boxSearch(pos, size, mask)
_bllua_ts.call('initContainerBoxSearch', posS, sizeS, maskS) _bllua_ts.call('initContainerBoxSearch', posS, sizeS, maskS)
return tsContainerSearchIterator return tsContainerSearchIterator
end end
function bl.radiusSearch(pos, radius, mask) function bl.radiusSearch(pos, radius, mask)
local posS = vecToTs(pos) local posS = vecToTs(pos)
if type(radius) ~= 'number' then if type(radius) ~= 'number' then
error('bl.radiusSearch: argument #2: radius must be a number', 2) end error('bl.radiusSearch: argument #2: radius must be a number', 2)
end
local radiusS = tostring(radius) local radiusS = tostring(radius)
local maskS = maskToTs(mask) local maskS = maskToTs(mask)
@@ -902,7 +976,8 @@ local function createTsObj(keyword, class, name, inherit, props)
local propsT = {} local propsT = {}
for k, v in pairs(props) do for k, v in pairs(props) do
if not isValidFuncName(k) then if not isValidFuncName(k) then
error('bl.new/bl.datablock: invalid property name \''..k..'\'') end error('bl.new/bl.datablock: invalid property name \'' .. k .. '\'')
end
table.insert(propsT, k .. '="' .. valToTs(v) .. '";') table.insert(propsT, k .. '="' .. valToTs(v) .. '";')
end end
@@ -912,7 +987,8 @@ local function createTsObj(keyword, class, name, inherit, props)
table.concat(propsT) .. '};') table.concat(propsT) .. '};')
local obj = toTsObject(objS) local obj = toTsObject(objS)
if not obj then if not obj then
error('bl.new/bl.datablock: failed to create object', 3) end error('bl.new/bl.datablock: failed to create object', 3)
end
return obj return obj
end end
@@ -937,13 +1013,15 @@ local function parseTsDecl(decl)
(inherit == nil or isValidFuncName(inherit))) then (inherit == nil or isValidFuncName(inherit))) then
error('bl.new/bl.datablock: invalid decl \'' .. decl .. '\'\n' .. error('bl.new/bl.datablock: invalid decl \'' .. decl .. '\'\n' ..
'must be of the format: \'className\', \'className name\', ' .. 'must be of the format: \'className\', \'className name\', ' ..
'\'className :inherit\', or \'className name:inherit\'', 3) end '\'className :inherit\', or \'className name:inherit\'', 3)
end
return class, name, inherit return class, name, inherit
end end
function bl.new(decl, props) function bl.new(decl, props)
local class, name, inherit = parseTsDecl(decl) local class, name, inherit = parseTsDecl(decl)
return createTsObj('new', class, name, inherit, props) return createTsObj('new', class, name, inherit, props)
end end
function bl.datablock(decl, props) function bl.datablock(decl, props)
local class, name, inherit = parseTsDecl(decl) local class, name, inherit = parseTsDecl(decl)
if not name then error('bl.datablock: must specify a name', 2) end if not name then error('bl.datablock: must specify a name', 2) end

View File

@@ -1,4 +1,3 @@
-- This Lua code provides some built-in utilities for writing Lua add-ons -- This Lua code provides some built-in utilities for writing Lua add-ons
-- It is eval'd automatically once BLLua3 has loaded the TS API and environment -- It is eval'd automatically once BLLua3 has loaded the TS API and environment
-- It only has access to the sandboxed lua environment, just like user code. -- It only has access to the sandboxed lua environment, just like user code.
@@ -9,6 +8,7 @@ ts = _bllua_ts
os = os or {} os = os or {}
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function os.time() return math.floor(tonumber(_bllua_ts.call('getSimTime')) / 1000) end function os.time() return math.floor(tonumber(_bllua_ts.call('getSimTime')) / 1000) end
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function os.clock() return tonumber(_bllua_ts.call('getSimTime')) / 1000 end function os.clock() return tonumber(_bllua_ts.call('getSimTime')) / 1000 end
@@ -84,7 +84,9 @@ local function new_file_obj(fn)
return file return file
end end
local function tflip(t) local u = {}; for _, n in ipairs(t) do u[n] = true end; return u; end local function tflip(t)
local u = {}; for _, n in ipairs(t) do u[n] = true end; return u;
end
local allowed_zip_dirs = tflip { local allowed_zip_dirs = tflip {
'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders' 'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders'
} }
@@ -100,7 +102,8 @@ local function io_open_absolute(fn, mode)
if not exist then return nil, err end if not exist then return nil, err end
if mode ~= nil and mode ~= 'r' and mode ~= 'rb' then if mode ~= nil and mode ~= 'r' and mode ~= 'rb' then
return nil, 'Files in zips can only be opened in read mode' end return nil, 'Files in zips can only be opened in read mode'
end
-- return a temp lua file object with the data -- return a temp lua file object with the data
local fi = new_file_obj(fn) local fi = new_file_obj(fn)
@@ -129,12 +132,14 @@ function io.open(fn, mode, errn)
return fi, err, fn return fi, err, fn
end end
end end
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function io.lines(fn) function io.lines(fn)
local fi, err, fn2 = io.open(fn, nil, 2) local fi, err, fn2 = io.open(fn, nil, 2)
if not fi then error('Error opening file \'' .. fn2 .. '\': ' .. err, 2) end if not fi then error('Error opening file \'' .. fn2 .. '\': ' .. err, 2) end
return fi:lines() return fi:lines()
end end
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function io.type(f) function io.type(f)
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
@@ -201,8 +206,11 @@ end
-- Exposure to TS -- Exposure to TS
function _bllua_getvar(name) return _G[name] end function _bllua_getvar(name) return _G[name] end
function _bllua_setvar(name, val) _G[name] = val end function _bllua_setvar(name, val) _G[name] = val end
function _bllua_eval(code) return loadstring(code)() end function _bllua_eval(code) return loadstring(code)() end
function _bllua_exec(fn) return dofile(fn, 2) end function _bllua_exec(fn) return dofile(fn, 2) end
local function isValidCode(code) local function isValidCode(code)
@@ -211,7 +219,8 @@ local function isValidCode(code)
end end
function _bllua_smarteval(code) function _bllua_smarteval(code)
if (not code:find('^print%(')) and isValidCode('print(' .. code .. ')') then if (not code:find('^print%(')) and isValidCode('print(' .. code .. ')') then
code = 'print('..code..')' end code = 'print(' .. code .. ')'
end
local f, e = loadstring(code) local f, e = loadstring(code)
if f then if f then
return f() return f()

View File

@@ -1,4 +1,3 @@
-- Basic functionality that should be standard in Lua -- Basic functionality that should be standard in Lua
@@ -7,6 +6,7 @@
function table.empty(t) function table.empty(t)
return next(t) ~= nil return next(t) ~= nil
end end
-- Apply a function to each key in a table -- Apply a function to each key in a table
function table.map(f, ...) function table.map(f, ...)
local ts = { ... } local ts = { ... }
@@ -18,6 +18,7 @@ function table.map(f, ...)
end end
return u return u
end end
function table.map_list(f, ...) function table.map_list(f, ...)
local ts = { ... } local ts = { ... }
local u = {} local u = {}
@@ -28,18 +29,21 @@ function table.map_list(f, ...)
end end
return u return u
end end
-- Swap keys/values -- Swap keys/values
function table.swap(t) function table.swap(t)
local u = {} local u = {}
for k, v in pairs(t) do u[v] = k end for k, v in pairs(t) do u[v] = k end
return u return u
end end
-- Reverse a list -- Reverse a list
function table.reverse(l) function table.reverse(l)
local m = {} local m = {}
for i = 1, #l do m[#l - i + 1] = l[i] end for i = 1, #l do m[#l - i + 1] = l[i] end
return m return m
end end
-- Whether a table is a list/array (has only monotonic integer keys) -- Whether a table is a list/array (has only monotonic integer keys)
function table.islist(t) function table.islist(t)
local n = 0 local n = 0
@@ -49,6 +53,7 @@ function table.islist(t)
end end
return n == #t return n == #t
end end
-- Append contents of other tables to first table -- Append contents of other tables to first table
function table.append(t, ...) function table.append(t, ...)
local a = { ... } local a = { ... }
@@ -57,6 +62,7 @@ function table.append(t, ...)
end end
return t return t
end end
-- Create a new table containing all keys from any number of tables -- Create a new table containing all keys from any number of tables
-- latter tables in the arg list override prior ones -- latter tables in the arg list override prior ones
-- overlaps, NOT appends, integer keys -- overlaps, NOT appends, integer keys
@@ -68,6 +74,7 @@ function table.join(...)
end end
return w return w
end end
-- Whether a table contains a certain value in any key -- Whether a table contains a certain value in any key
function table.contains(t, s) function table.contains(t, s)
for _, v in pairs(t) do for _, v in pairs(t) do
@@ -75,29 +82,34 @@ function table.contains(t,s)
end end
return false return false
end end
function table.contains_list(t, s) function table.contains_list(t, s)
for _, v in ipairs(t) do for _, v in ipairs(t) do
if v == s then return true end if v == s then return true end
end end
return false return false
end end
-- Copy a table to another table -- Copy a table to another table
function table.copy(t) function table.copy(t)
local u = {} local u = {}
for k, v in pairs(t) do u[k] = v end for k, v in pairs(t) do u[k] = v end
return u return u
end end
function table.copy_list(l) function table.copy_list(l)
local m = {} local m = {}
for i, v in ipairs(l) do m[i] = v end for i, v in ipairs(l) do m[i] = v end
return m return m
end end
-- Sort a table in a new copy -- Sort a table in a new copy
function table.sortcopy(t, f) function table.sortcopy(t, f)
local u = table.copy_list(t) local u = table.copy_list(t)
table.sort(u, f) table.sort(u, f)
return u return u
end end
-- Remove a value from a table -- Remove a value from a table
function table.removevalue(t, r) function table.removevalue(t, r)
local rem = {} local rem = {}
@@ -106,6 +118,7 @@ function table.removevalue(t, r)
end end
for _, k in ipairs(rem) do t[k] = nil end for _, k in ipairs(rem) do t[k] = nil end
end end
function table.removevalue_list(t, r) function table.removevalue_list(t, r)
for i = #t, 1, -1 do for i = #t, 1, -1 do
if t[i] == r then if t[i] == r then
@@ -113,6 +126,7 @@ function table.removevalue_list(t, r)
end end
end end
end end
-- Export tables into formatted executable strings -- Export tables into formatted executable strings
local function tabs(tabLevel) local function tabs(tabLevel)
return (' '):rep(tabLevel) return (' '):rep(tabLevel)
@@ -186,7 +200,6 @@ function table.tostring(t)
return tableToString(t, 0, {}) return tableToString(t, 0, {})
end end
-- String -- String
-- Split string into table by separator -- Split string into table by separator
@@ -194,7 +207,8 @@ end
-- if regex is not true, sep is treated as a regex pattern -- if regex is not true, sep is treated as a regex pattern
function string.split(str, sep, noregex) function string.split(str, sep, noregex)
if type(str) ~= 'string' then if type(str) ~= 'string' then
error('string.split: argument #1: expected string, got '..type(str), 2) end error('string.split: argument #1: expected string, got ' .. type(str), 2)
end
if sep == nil or sep == '' then if sep == nil or sep == '' then
local t = {} local t = {}
local ns = #str local ns = #str
@@ -220,6 +234,7 @@ function string.split(str, sep, noregex)
'string.split: argument #2: expected string or nil, got ' .. type(sep), 2) 'string.split: argument #2: expected string or nil, got ' .. type(sep), 2)
end end
end end
-- Split string to a list of char bytes -- Split string to a list of char bytes
function string.bytes(s) function string.bytes(s)
local b = {} local b = {}
@@ -229,11 +244,13 @@ function string.bytes(s)
end end
return b return b
end end
-- Trim leading and trailing whitespace -- Trim leading and trailing whitespace
function string.trim(s, ws) function string.trim(s, ws)
ws = ws or ' \t\r\n' ws = ws or ' \t\r\n'
return s:gsub('^[' .. ws .. ']+', ''):gsub('[' .. ws .. ']+$', '') .. '' return s:gsub('^[' .. ws .. ']+', ''):gsub('[' .. ws .. ']+$', '') .. ''
end end
-- String slicing and searching using [] operator -- String slicing and searching using [] operator
local str_meta = getmetatable('') local str_meta = getmetatable('')
local str_meta_index_old = str_meta.__index local str_meta_index_old = str_meta.__index
@@ -249,15 +266,20 @@ function str_meta.__index(s,k)
return string.sub(s, a, b) return string.sub(s, a, b)
end end
end end
-- String iterator -- String iterator
function string.chars(s) function string.chars(s)
local i = 0 local i = 0
return function() return function()
i = i + 1 i = i + 1
if i<=#s then return s:sub(i,i) if i <= #s then
else return nil end return s:sub(i, i)
else
return nil
end end
end end
end
-- Escape sequences -- Escape sequences
local defaultEscapes = { local defaultEscapes = {
['\\'] = '\\\\', ['\\'] = '\\\\',
@@ -277,6 +299,7 @@ function string.escape(s, escapes)
end end
return table.concat(t) return table.concat(t)
end end
local defaultEscapeChar = '\\' local defaultEscapeChar = '\\'
local defaultUnescapes = { local defaultUnescapes = {
['\\'] = '\\', ['\\'] = '\\',
@@ -307,7 +330,6 @@ function string.unescape(s, escapeChar, unescapes)
return table.concat(t) return table.concat(t)
end end
-- IO -- IO
io = io or {} io = io or {}
@@ -319,6 +341,7 @@ function io.readall(filename)
fi:close() fi:close()
return s return s
end end
-- Write data to file all at once, return true if success / false,err if failure -- Write data to file all at once, return true if success / false,err if failure
function io.writeall(filename, data) function io.writeall(filename, data)
local fi, err = io.open(filename, 'wb') local fi, err = io.open(filename, 'wb')
@@ -328,19 +351,23 @@ function io.writeall(filename, data)
return true, nil return true, nil
end end
-- Math -- Math
-- Round -- Round
function math.round(x) function math.round(x)
return math.floor(x + 0.5) return math.floor(x + 0.5)
end end
-- Mod that accounts for floating point inaccuracy -- Mod that accounts for floating point inaccuracy
function math.mod(a, b) function math.mod(a, b)
local m = a % b local m = a % b
if m==0 or math.abs(m)<1e-15 or math.abs(m-b)<1e-15 then return 0 if m == 0 or math.abs(m) < 1e-15 or math.abs(m - b) < 1e-15 then
else return m end return 0
else
return m
end end
end
-- Clamp value between min and max -- Clamp value between min and max
function math.clamp(v, n, x) function math.clamp(v, n, x)
return math.min(x, math.max(v, n)) return math.min(x, math.max(v, n))

View File

@@ -1,18 +1,19 @@
-- Vector math class with operators -- Vector math class with operators
local vector_meta local vector_meta
local vector_new local vector_new
local function vector_check(v, n, name, argn) local function vector_check(v, n, name, argn)
if not v.__is_vector then if not v.__is_vector then
error('vector ' .. name .. ': argument #' .. (argn or 1) error('vector ' .. name .. ': argument #' .. (argn or 1)
..': expected vector, got '..type(v), n+1) end .. ': expected vector, got ' .. type(v), n + 1)
end
end end
local function vector_checksamelen(v1, v2, name) local function vector_checksamelen(v1, v2, name)
vector_check(v1, 3, name, 1) vector_check(v1, 3, name, 1)
vector_check(v2, 3, name, 2) vector_check(v2, 3, name, 2)
if #v1 ~= #v2 then if #v1 ~= #v2 then
error('vector ' .. name .. ': vector lengths do not match (lengths are ' error('vector ' .. name .. ': vector lengths do not match (lengths are '
..#v1..' and '..#v2..')', 3) end .. #v1 .. ' and ' .. #v2 .. ')', 3)
end
return #v1 return #v1
end end
local function vector_checklen(v1, v2, name, len) local function vector_checklen(v1, v2, name, len)
@@ -20,7 +21,8 @@ local function vector_checklen(v1, v2, name, len)
vector_check(v2, 3, name, 2) vector_check(v2, 3, name, 2)
if #v1 ~= len or #v2 ~= len then if #v1 ~= len or #v2 ~= len then
error('vector ' .. name .. ': vector lengths are not ' .. len .. ' (lengths are ' error('vector ' .. name .. ': vector lengths are not ' .. len .. ' (lengths are '
..#v1..' and '..#v2..')', 3) end .. #v1 .. ' and ' .. #v2 .. ')', 3)
end
end end
local function vector_opnnn(name, op) local function vector_opnnn(name, op)
return function(v1, v2) return function(v1, v2)
@@ -69,15 +71,21 @@ local vector_indices = {x = 1, y = 2, z = 3, w = 4, r = 1, g = 2, b = 3, a = 4}
local vector_meta = { local vector_meta = {
__is_vector = true, __is_vector = true,
__index = function(t, k) __index = function(t, k)
if tonumber(k) then return rawget(t, k) if tonumber(k) then
elseif vector_indices[k] then return rawget(t, vector_indices[k]) return rawget(t, k)
else return getmetatable(t)[k] elseif vector_indices[k] then
return rawget(t, vector_indices[k])
else
return getmetatable(t)[k]
end end
end, end,
__newindex = function(t, k, v) __newindex = function(t, k, v)
if tonumber(k) then rawset(t, k, v) if tonumber(k) then
elseif vector_indices[k] then rawset(t, vector_indices[k], v) rawset(t, k, v)
else return elseif vector_indices[k] then
rawset(t, vector_indices[k], v)
else
return
end end
end, end,
__add = vector_opnnn('add', function(x1, x2) return x1 + x2 end), __add = vector_opnnn('add', function(x1, x2) return x1 + x2 end),
@@ -115,8 +123,11 @@ local vector_meta = {
local len = #v1 local len = #v1
local v3 = {} local v3 = {}
for i = 1, len do for i = 1, len do
if length==0 then v3[i] = 0 if length == 0 then
else v3[i] = v1[i]/length end v3[i] = 0
else
v3[i] = v1[i] / length
end
end end
return vector_new(v3) return vector_new(v3)
end, end,
@@ -152,20 +163,28 @@ local vector_meta = {
rotateByAngleId = function(v1, r) rotateByAngleId = function(v1, r)
--vector_check(v1, 2, 'rotate') --vector_check(v1, 2, 'rotate')
if type(r) ~= 'number' or r % 1 ~= 0 then if type(r) ~= 'number' or r % 1 ~= 0 then
error('vector rotateByAngleId: invalid rotation '..tostring(r), 2) end error('vector rotateByAngleId: invalid rotation ' .. tostring(r), 2)
end
r = r % 4 r = r % 4
local v2 local v2
if r==0 then v2 = vector_new{ v1[1], v1[2], v1[3] } if r == 0 then
elseif r==1 then v2 = vector_new{ v1[2], -v1[1], v1[3] } v2 = vector_new { v1[1], v1[2], v1[3] }
elseif r==2 then v2 = vector_new{ -v1[1], -v1[2], v1[3] } elseif r == 1 then
elseif r==3 then v2 = vector_new{ -v1[2], v1[1], v1[3] } v2 = vector_new { v1[2], -v1[1], v1[3] }
else error('vector rotateByAngleId: invalid rotation '..r, 2) end elseif r == 2 then
v2 = vector_new { -v1[1], -v1[2], v1[3] }
elseif r == 3 then
v2 = vector_new { -v1[2], v1[1], v1[3] }
else
error('vector rotateByAngleId: invalid rotation ' .. r, 2)
end
return v2 return v2
end, end,
rotateZ = function(v, r) rotateZ = function(v, r)
--vector_check(v, 2, 'rotate2d') --vector_check(v, 2, 'rotate2d')
if type(r) ~= 'number' then if type(r) ~= 'number' then
error('vector rotateZ: invalid rotation '..tostring(r), 2) end error('vector rotateZ: invalid rotation ' .. tostring(r), 2)
end
local len = math.sqrt(v[1] ^ 2 + v[2] ^ 2) local len = math.sqrt(v[1] ^ 2 + v[2] ^ 2)
local ang = math.atan2(v[2], v[1]) + r local ang = math.atan2(v[2], v[1]) + r
local v2 = vector_new { math.cos(ang) * len, math.sin(ang) * len } local v2 = vector_new { math.cos(ang) * len, math.sin(ang) * len }