diff --git a/BlockLua.dll b/BlockLua.dll index 3546521..0935a5b 100644 Binary files a/BlockLua.dll and b/BlockLua.dll differ diff --git a/readme.md b/readme.md index f54d2d9..24fce77 100644 --- a/readme.md +++ b/readme.md @@ -143,7 +143,9 @@ WIP `string.trim(str, charsToTrim=' \t\r\n')` `table.empty` `table.map(func, ...)` +`table.mapk(func, ...)` `table.map_list(func, ...)` +`table.mapi_list(func, ...)` `table.swap(tbl)` `table.reverse(list)` `table.islist(list)` @@ -173,22 +175,23 @@ TorqueScript stores no type information; all values in TorqueScript are strings. ### From Lua to TorqueScript - `nil` becomes the empty string "" - `true` and `false` become "1" and "0" respectively -- Torque containers become their object ID +- A Torque object container becomes its object ID - A `vector` becomes a string containing three numbers separated by spaces -- A table of two vectors becomes a string containing six numbers separated by spaces +- A table of two `vector`s becomes a string containing six numbers separated by spaces +- (WIP) A `matrix` is converted into an axis-angle (a "transform"), a string containing seven numbers separated by spaces - Any `string` is passed directly as a string - Tables cannot be passed and will throw an error ### From TorqueScript to Lua -- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container. - The empty string "" becomes `nil` +- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container. - A string containing two or three numbers separated by single spaces becomes a `vector` - A string containing six numbers separated by single spaces becomes a table of two vectors, usually defining the corners a bounding box -- (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform" in TorqueScript parlance), and is converted into a `matrix` representing the translation and rotation. +- (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform"), and is converted into a `matrix` representing the translation and rotation - Any other string is passed directly as a `string` For scenarios where the automatic TorqueScript->Lua conversion rules are insufficient or incorrect, use `bl.type`. -To convert objects by hand, use `bl.object`, `bl.boolean`, or `bl.string`. +To convert things by hand, use `bl.object`, `bl.boolean`, or `bl.string`. ## I/O and Safety All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is. diff --git a/src/util/libbl.lua b/src/util/libbl.lua index 878522a..c84806e 100644 --- a/src/util/libbl.lua +++ b/src/util/libbl.lua @@ -6,15 +6,30 @@ local _bllua_ts = ts bl = bl or {} +-- Config +local tsMaxArgs = 16 +local tsArgsLocal = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p' +local tsArgsGlobal = + '$_bllua_hook_arg1,$_bllua_hook_arg2,$_bllua_hook_arg3,$_bllua_hook_arg4,'.. + '$_bllua_hook_arg5,$_bllua_hook_arg6,$_bllua_hook_arg7,$_bllua_hook_arg8,'.. + '$_bllua_hook_arg9,$_bllua_hook_arg10,$_bllua_hook_arg11,$_bllua_hook_arg12,'.. + '$_bllua_hook_arg13,$_bllua_hook_arg14,$_bllua_hook_arg15,$_bllua_hook_arg16' + -- Misc --- Apply a function to each element in a list, building a new list from the returns -local function map(t,f) - local u = {} - for i,v in ipairs(t) do - u[i] = f(v) +local function ipairsNilable(t) + local maxk = 0 + for k,_ in pairs(t) do + if k>maxk then maxk = k end + end + local i = 0 + return function() + i = i+1 + if i>maxk then return nil + else return i, t[i] end end - return u end + +-- Validation local function isValidFuncName(name) return type(name)=='string' and name:find('^[a-zA-Z_][a-zA-Z0-9_]*$') end @@ -184,13 +199,17 @@ local function valFromTs(val, name, name2) end local function arglistFromTs(name, argsS) local args = {} - for i,arg in ipairs(argsS) do + for i,arg in ipairsNilable(argsS) do args[i] = valFromTs(arg, name..':'..i) end return args end local function arglistToTs(args) - return map(args, valToTs) + local argsS = {} + for i,v in ipairsNilable(args) do + table.insert(argsS, valToTs(v)) + end + return argsS end local function classFromForceTypeStr(name) local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$') @@ -549,14 +568,13 @@ end local function safeNamespaceName(name) return tostring(name:gsub(':', '_')) end -local nscallArgStr = '%a,%b,%c,%d,%e,%f,%g,%h' bl._cachedNamespaceCalls = {} local function tsNamespacedCallTfname(name) local tfname = bl._cachedNamespaceCalls[name] if not tfname then tfname = '_bllua_nscall_'..safeNamespaceName(name) - local tfcode = 'function '..tfname..'('..nscallArgStr..'){'.. - name..'('..nscallArgStr..');}' + local tfcode = 'function '..tfname..'('..tsArgsLocal..'){'.. + name..'('..tsArgsLocal..');}' _bllua_ts.eval(tfcode) bl._cachedNamespaceCalls[name] = tfname end @@ -753,9 +771,8 @@ local function addCmd(cmd, func) if not isValidFuncName(cmd) then error('addCmd: invalid function name \''..tostring(cmd)..'\'') end bl._cmds[cmd] = func - 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_luacall(_bllua_process_cmd,"'..cmd..'",'..arglist..');}') + _bllua_ts.eval('function '..cmd..'('..tsArgsLocal..'){'.. + '_bllua_luacall(_bllua_process_cmd,"'..cmd..'",'..tsArgsLocal..');}') end function bl.addServerCmd(name, func) name = name:lower() @@ -803,9 +820,6 @@ local function deactivatePackage(pkg) _bllua_ts.call('deactivatePackage', pkg) end end -local hookNargs = 8 -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' bl._hooks = bl._hooks or {} function _bllua_process_hook_before(pkgS, nameS, ...) local args = arglistFromTs(nameS, {...}) @@ -819,7 +833,7 @@ function _bllua_process_hook_before(pkgS, nameS, ...) _bllua_ts.setvar('_bllua_hook_abort', '1') _bllua_ts.setvar('_bllua_hook_return', valToTs(args._return)) end - for i=1,hookNargs do + for i=1,tsMaxArgs do _bllua_ts.setvar('_bllua_hook_arg'..i, valToTs(args[i])) end end @@ -836,13 +850,13 @@ function _bllua_process_hook_after(pkgS, nameS, resultS, ...) end local function updateHook(pkg, name, hk) local beforeCode = hk.before and - ('_bllua_luacall("_bllua_process_hook_before", "'..pkg..'","'..name.. - '",'..hookArglistLocal..');') or '' - local arglist = (hk.before and hookArglistGlobal or hookArglistLocal) + ('_bllua_luacall("_bllua_process_hook_before","'..pkg..'","'..name.. + '",'..tsArgsLocal..');') or '' + local arglist = (hk.before and tsArgsGlobal or tsArgsLocal) local parentCode = tsIsFunctionNsname(name) and -- only call parent if it exists (hk.before and - 'if($_bllua_hook_abort)return $_bllua_hook_return; else ' or '').. + 'if($_bllua_hook_abort)return $_bllua_hook_return;else ' or '').. ((hk.after and '%result=' or 'return ').. 'parent::'..name:match('[^:]+$').. '('..arglist..');') or '' @@ -851,10 +865,11 @@ local function updateHook(pkg, name, hk) arglist..');') or '' local code = 'package '..pkg..'{'.. - 'function '..name..'('..hookArglistLocal..'){'.. + 'function '..name..'('..tsArgsLocal..'){'.. beforeCode..parentCode..afterCode.. '}'.. '};' + print('bl.hook eval output: [['..code..']]') _bllua_ts.eval(code) end function bl.hook(pkg, name, time, func) @@ -880,9 +895,9 @@ end function bl.unhook(pkg, name, time) if not isValidFuncName(pkg) then error('bl.unhook: argument #1: invalid package name \''..tostring(pkg)..'\'', 2) end - if not isValidFuncNameNs(name) then + if name and not isValidFuncNameNs(name) then error('bl.unhook: argument #2: invalid function name \''..tostring(name)..'\'', 2) end - if time~='before' and time~='after' then + if time and time~='before' and time~='after' then error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2) end if not name then @@ -909,10 +924,13 @@ function bl.unhook(pkg, name, time) if time~='before' and time~='after' then error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2) end bl._hooks[pkg][name][time] = nil - if tableEmpty(bl._hooks[pkg][name]) and tableEmpty(bl._hooks[pkg]) then + if tableEmpty(bl._hooks[pkg][name]) then + bl._hooks[pkg][name] = nil + end + if tableEmpty(bl._hooks[pkg]) then bl._hooks[pkg] = nil - deactivatePackage(pkg) updateHook(pkg, name, {}) + deactivatePackage(pkg) else updateHook(pkg, name, bl._hooks[pkg][name]) end @@ -968,7 +986,7 @@ function bl.raycast(start, stop, mask, ignores) local stopS = vecToTs(start) local maskS = maskToTs(mask) local ignoresS = {} - for _,v in ipairs(ignores) do + for _,v in ipairsNilable(ignores) do table.insert(ignoresS, objToTs(v)) end @@ -1017,7 +1035,7 @@ end local maxTsArgLen = 8192 local function valsToString(vals) local strs = {} - for i,v in ipairs(vals) do + for i,v in ipairsNilable(vals) do local tstr = table.tostring(v) if #tstr>maxTsArgLen then tstr = tostring(v) diff --git a/src/util/std.lua b/src/util/std.lua index 53e9e84..16a2d92 100644 --- a/src/util/std.lua +++ b/src/util/std.lua @@ -13,8 +13,18 @@ function table.map(f, ...) local u = {} for k,_ in pairs(ts[1]) do local args = {} - for j=1,#ts do args[j] = ts[j][i] end - u[i] = f(unpack(args)) + for j=1,#ts do args[j] = ts[j][k] end + u[k] = f(unpack(args)) + end + return u +end +function table.mapk(f, ...) + local ts = {...} + local u = {} + for k,_ in pairs(ts[1]) do + local args = {} + for j=1,#ts do args[j] = ts[j][k] end + u[k] = f(k, unpack(args)) end return u end @@ -28,6 +38,16 @@ function table.map_list(f, ...) end return u end +function table.mapi_list(f, ...) + local ts = {...} + local u = {} + for i=1,#ts[1] do + local args = {} + for j=1,#ts do args[j] = ts[j][i] end + u[i] = f(i, unpack(args)) + end + return u +end -- Swap keys/values function table.swap(t) local u = {}