From 76c758a47b6b8f5d0ec9a3d65f554c05cb71b343 Mon Sep 17 00:00:00 2001 From: Redo Date: Mon, 6 Oct 2025 10:30:25 -0500 Subject: [PATCH] allow luaget/luaset/luacall to access tables and methods, improve type conversion, add bl.string, begin adding matrix --- BlockLua.dll | Bin 375335 -> 378956 bytes readme.md | 30 ++++-- src/bllua4.cpp | 2 + src/lua-env-safe.lua | 3 +- src/lua-env.lua | 3 +- src/util/libbl-support.cs | 22 ++++ src/util/libbl.lua | 214 ++++++++++++++++++++++++++++---------- src/util/libts-lua.lua | 6 -- src/util/libts-ts.cs | 22 ---- src/util/matrix.lua | 7 ++ src/util/std.lua | 5 +- src/util/vector.lua | 3 + 12 files changed, 223 insertions(+), 94 deletions(-) create mode 100644 src/util/matrix.lua diff --git a/BlockLua.dll b/BlockLua.dll index 58d6e4573dc1ffa06b4c883b730660ac21e85ae3..a11bb18335f804d638bf3b4d7a73cee0418b6557 100644 GIT binary patch delta 25339 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 delta 22247 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 Tweu~7dqgjnmgn}1@Am#bG8MZy diff --git a/readme.md b/readme.md index 1e5e5c2..e78f471 100644 --- a/readme.md +++ b/readme.md @@ -36,6 +36,8 @@ Lua scripting for Blockland `object:method(args)` - Call a Torque object method `object[index]` - Access a member of a Torque set or group `for childIndex, child in object:members() do` - Iterate objects within of a Torque set or group. Indices start at 0 like in Torque. +`bl.isObject(object, objectID, or 'objectName')` - Check if an object exists +`object:exists()` - Check if an object exists ### Timing/Schedules `sched = bl.schedule(timeMs, function, args...)` - Schedule a Lua function to be called later, similar to schedule in Torque @@ -101,10 +103,11 @@ When reading from outside ZIPs, binary files are fully supported. `bl.type('className::funcName', 'type')` - Register the return type of a Torque object method. `bl.class('className')` - Register an existing Torque class to be used from Lua. Already done for all built-in classes. `bl.class('className', 'parentClassName')` - Same as above, with inheritance -`bl.boolean(thing)` - Manually convert a Torque boolean (0 or 1) into a Lua boolean. -`bl.object(thing)` - Manually convert a Torque object reference (object ID or name) into a Lua object. +`bl.boolean(arg)` - Manually convert a Torque boolean (0 or 1) into a Lua boolean. +`bl.object(arg)` - Manually convert a Torque object reference (object ID or name) into a Lua object. +`bl.string(arg)` - Manually convert any automatically-converted Torque value back into a string. This is not as reliable as using `bl.type` to specify the type as a string beforehand. -### Vectors +### Vector `vec = vector{x,y,z}` - Create a vector. Can have any number of elements `vec1 + vec2` - Add `vec1 - vec2` - Subtract @@ -129,6 +132,9 @@ When reading from outside ZIPs, binary files are fully supported. `vec1:distance(vec2)` - Distance between two points `vec2 = vec:copy()` - Clone a vector so its elements can be modified without affecting the original. Usually not needed - the builtin vector functions never modify vectors in-place. +### Matrix +WIP + ### Extended Standard Lua Library `string[index]` `string[{start,stop}]` @@ -163,12 +169,7 @@ When reading from outside ZIPs, binary files are fully supported. ## Type Conversion When a TorqueScript function is called from Lua or vice-versa, the arguments and return value must be converted between the two languages' type systems. TorqueScript stores no type information; all values in TorqueScript are strings. So it's necessary to make some inferences when converting values between the two languages. -### 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` -- A string containing three numbers separated by spaces becomes a `vector` -- A string containing six numbers separated by spaces becomes a table of two vectors -- Any other string is passed directly as a `string` + ### From Lua to TorqueScript - `nil` becomes the empty string "" - `true` and `false` become "1" and "0" respectively @@ -178,6 +179,17 @@ TorqueScript stores no type information; all values in TorqueScript are strings. - 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` +- 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. +- 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`. + ## I/O and Safety All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is. BlockLua also has access to any C libraries installed in the `modules/lualib` folder, so be careful throwing things in there. diff --git a/src/bllua4.cpp b/src/bllua4.cpp index c8955fe..5e1d94b 100644 --- a/src/bllua4.cpp +++ b/src/bllua4.cpp @@ -28,6 +28,7 @@ INCLUDE_BIN(bll_fileLuaEnv , "lua-env.lua"); INCLUDE_BIN(bll_fileTsEnv , "ts-env.cs" ); INCLUDE_BIN(bll_fileLuaStd , "util/std.lua"); INCLUDE_BIN(bll_fileLuaVector , "util/vector.lua"); +INCLUDE_BIN(bll_fileLuaMatrix , "util/matrix.lua"); INCLUDE_BIN(bll_fileLuaLibts , "util/libts-lua.lua"); INCLUDE_BIN(bll_fileTsLibts , "util/libts-ts.cs"); INCLUDE_BIN(bll_fileLuaLibbl , "util/libbl.lua" ); @@ -67,6 +68,7 @@ bool init() { // Load utilities BLL_LOAD_LUA(gL, bll_fileLuaStd); BLL_LOAD_LUA(gL, bll_fileLuaVector); + BLL_LOAD_LUA(gL, bll_fileLuaMatrix); BLL_LOAD_LUA(gL, bll_fileLuaLibts); BlEval(bll_fileTsLibts); BLL_LOAD_LUA(gL, bll_fileLuaLibbl); diff --git a/src/lua-env-safe.lua b/src/lua-env-safe.lua index c64a30e..adb5275 100644 --- a/src/lua-env-safe.lua +++ b/src/lua-env-safe.lua @@ -139,4 +139,5 @@ debug = { getfilename = old_debug.getfilename, -- defined in lua.env.lua } -_bllua_ts.echo(' Executed bllua-env-safe.lua') +print = _bllua_ts.echo +print(' Executed bllua-env-safe.lua') diff --git a/src/lua-env.lua b/src/lua-env.lua index f485ee2..e16f7c2 100644 --- a/src/lua-env.lua +++ b/src/lua-env.lua @@ -37,4 +37,5 @@ function _bllua_on_error(err) return table.concat(tracelines, '\n') end -_bllua_ts.echo(' Executed bllua-env.lua') +print = _bllua_ts.echo +print(' Executed bllua-env.lua') diff --git a/src/util/libbl-support.cs b/src/util/libbl-support.cs index c4032d0..873b50b 100644 --- a/src/util/libbl-support.cs +++ b/src/util/libbl-support.cs @@ -42,4 +42,26 @@ package _bllua_objectDeletionHook { }; activatePackage(_bllua_objectDeletionHook); +// Public Lua library for TS +function luacall(%func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p) { + if($_bllua_active) + return _bllua_luacall("_bllua_call", %func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p); +} +function luaexec(%fn) { + if($_bllua_active) + return _bllua_luacall("_bllua_exec", %fn); +} +function luaeval(%code) { + if($_bllua_active) + return _bllua_luacall("_bllua_eval", %code); +} +function luaget(%name) { + if($_bllua_active) + return _bllua_luacall("_bllua_getvar", %name); +} +function luaset(%name, %val) { + if($_bllua_active) + _bllua_luacall("_bllua_setvar", %name, %val); +} + echo(" Executed libbl-support.cs"); diff --git a/src/util/libbl.lua b/src/util/libbl.lua index efd5889..52195df 100644 --- a/src/util/libbl.lua +++ b/src/util/libbl.lua @@ -2,8 +2,6 @@ -- Main lua-side functionality of bllua, -- provided through the global table 'bl.' --- todo: set - local _bllua_ts = ts bl = bl or {} @@ -18,7 +16,7 @@ local function map(t,f) return u end local function isValidFuncName(name) - return type(name)=='string' and name:find('^[a-zA-Z0-9_]+$') + return type(name)=='string' and name:find('^[a-zA-Z_][a-zA-Z0-9_]*$') end local function isValidFuncNameNs(name) return type(name)=='string' and ( @@ -76,7 +74,7 @@ for k,v in pairs(tsTypesByName) do tsTypesByNum[v] = k end --- Type conversion +-- Type conversion from Lua to TS local toTsObject -- Convert a string from TS into a boolean -- Note: Nonempty nonnumeric strings evaluate to 1, unlike in TS @@ -100,21 +98,61 @@ local function valToTs(val) -- box - > 6 numbers return table.concat(val[1], ' ')..' '..table.concat(val[2], ' ') else - error('valToTs: could not convert table', 3) + error('valToTs: cannot pass Lua tables to TorqueScript.', 3) end else - error('valToTs: could not convert '..type(val), 3) + error('valToTs: could not convert value to TorqueScript: '..tostring(val), 3) end end + +-- Type conversion from TS to Lua local fromTsForceTypes = { ['boolean'] = tsBool, - ['object'] = function(val) toTsObject(val) end, -- toTsObject not defined yet - ['string'] = tostring, + ['object'] = function(val) toTsObject(val) end, -- wrap because toTsObject not defined yet + ['string'] = function(val) return val end, } -local function convertValFromTs(val, typ) +local function forceValFromTs(val, typ) return fromTsForceTypes[typ](val) or error('valFromTs: invalid force type '..typ, 4) end +local function vectorFromTs(val) + local xS,yS,zS = val:match('^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') + if xS then return vector{tonumber(xS),tonumber(yS),tonumber(zS)} + else return nil end +end +local function boxFromTs(val) + local x1S,y1S,z1S,x2S,y2S,z2S = val:match( + '^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) '.. + '(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') + if x1S then return { + vector{tonumber(x1S),tonumber(y1S),tonumber(z1S)}, + vector{tonumber(x2S),tonumber(y2S),tonumber(z2S)} } + else return nil end +end +local function multinumericFromTs(val) + local tsNumPat = '%-?[0-9]+%.?[0-9]*e?[0-9]*' + if val:find('^'..tsNumPat) then + local nums = {} + for _,part in ipairs(val:split(' ')) do + if part:find('^'..tsNumPat..'$') then + table.insert(nums, tonumber(part)) + else + return nil + end + end + if #nums==2 or #nums==3 then + return vector(nums) + elseif #nums==6 then -- box + return { + vector{nums[1], nums[2], nums[3]}, + vector{nums[4], nums[5], nums[6]} } + elseif #nums==7 then -- axis + return nil --return matrix(nums) + else + return nil + end + end +end bl._forceType = bl._forceType or {} local function valFromTs(val, name, name2) -- todo: ensure name and name2 are already lowercase if type(val)~='string' then @@ -122,13 +160,13 @@ local function valFromTs(val, name, name2) -- todo: ensure name and name2 are al if name then name = name:lower() if bl._forceType[name] then - return convertValFromTs(val, bl._forceType[name]) + return forceValFromTs(val, bl._forceType[name]) end end if name2 then name2 = name2:lower() if bl._forceType[name2] then - return convertValFromTs(val, bl._forceType[name2]) + return forceValFromTs(val, bl._forceType[name2]) end end -- '' -> nil @@ -136,16 +174,9 @@ local function valFromTs(val, name, name2) -- todo: ensure name and name2 are al -- number local num = tonumber(val) if num then return num end - -- vector - local xS,yS,zS = val:match('^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') - if xS then return vector{tonumber(xS),tonumber(yS),tonumber(zS)} end - local x1S,y1S,z1S,x2S,y2S,z2S = val:match( - '^(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+) '.. - '(%-?[0-9%.e]+) (%-?[0-9%.e]+) (%-?[0-9%.e]+)$') - -- box (2 vectors) - if x1S then return { - vector{tonumber(x1S),tonumber(y1S),tonumber(z1S)}, - vector{tonumber(x2S),tonumber(y2S),tonumber(z2S)} } end + -- vector, box, or axis->matrix + local vec = multinumericFromTs(val) + if vec then return vec end -- string return val end @@ -189,13 +220,33 @@ setForceType = function(ftname, typ) end bl.type = setForceType +-- Type conversion TS->Lua backwards, convert back to string +local function numFromTsTostring(val) + -- todo: as-good-as-possible scientific notation for numbers + return tostring(val) +end +local function valFromTsTostring(val) + if type(val)=='string' then + return val + elseif type(val)=='number' then + return numFromTsTostring(val) + elseif type(val)=='table' then + if val.__is_vector then + local strs = {} + for i=1,#val do strs[i] = numFromTsTostring(val[i]) end + return table.concat(strs, ' ') + elseif val.__is_matrix then + -- todo: matrix back to axis-angle string + error('bl.string: matrix not yet supported', 3) + end + end + error('bl.string: cannot convert \''..type(val)..'\'', 3) +end --- Value detection - +-- Getting info from TS about functions and objects local function isTsObject(t) return type(t)=='table' and t._tsObjectId~=nil end - local function tsIsObject(name) return tsBool(_bllua_ts.call('isObject', name)) end local function tsIsFunction(name) return tsBool(_bllua_ts.call('isFunction', name)) end local function tsIsFunctionNs(ns, name) return tsBool(_bllua_ts.call('isFunction', ns, name)) end @@ -204,27 +255,31 @@ local function tsIsFunctionNsname(nsname) if ns then return tsIsFunctionNs(ns, name) else return tsIsFunction(nsname) end end - +-- sanity check to make sure objects that don't isObject are always marked as ._deleted +-- can be removed later +local function assertTsObjectExists(obj) + local is = tsIsObject(obj._tsObjectId) + if not is then + print('Warning: TS object :exists or isobject from lua but no longer exists') end + return is +end function bl.isObject(obj) - if isTsObject(obj) then - obj = obj._tsObjectId - elseif type(obj)=='number' then - obj = tostring(obj) - elseif type(obj)~='string' then + if type(obj)=='number' then -- object id + return tsIsObject(tostring(obj)) + elseif type(obj)=='string' then -- object name + return tsIsObject(obj) + elseif isTsObject(obj) then -- lua object + if obj._deleted then + return false + else + return assertTsObjectExists(obj) + end + else error('bl.isObject: argument #1: expected torque object, number, or string', 2) end - return tsIsObject(obj) end -function bl.isFunction(a1, a2) - if type(a1)~='string' then - error('bl.isFunction: argument #1: expected string', 2) end - if a2 then - if type(a2)~='string' then - error('bl.isFunction: argument #2: expected string', 2) end - return tsIsFunctionNs(a1, a2) - else - return tsIsFunction(a1) - end +function bl.isFunction(name) + return tsIsFunctionNsname(name) end -- Torque object pseudo-class @@ -350,7 +405,8 @@ local tsObjectMeta = { -- Display a nice info string __tostring = function(t) return 'torque:'..t._tsNamespace..':'..t._tsObjectId.. - (t._tsName~='' and ('('..t._tsName..')') or '') + (t._tsName~='' and ('('..t._tsName..')') or '').. + (t._deleted and '(deleted)' or '') end, -- #object -- If the object has a getCount method, return its count @@ -430,8 +486,10 @@ local tsObjectMeta = { if t==nil then error('ts object method: be sure to use :func() not .func()', 2) end if t._deleted then - return false end - return tsIsObject(t._tsObjectId) + return false + else + return assertTsObjectExists(t) + end end, } -- Weak-values table for caching Torque object references @@ -464,7 +522,7 @@ toTsObject = function(idiS) idiS = idiS:lower() if bl._objectsWeak[idiS] then return bl._objectsWeak[idiS] end - if not tsBool(_bllua_ts.call('isObject', idiS)) then + if not tsIsObject(idiS) then --error('toTsObject: object \''..idiS..'\' does not exist', 2) end return nil end @@ -560,6 +618,10 @@ end function bl.exec(file) return valFromTs(_bllua_ts.call('exec', file)) end +function bl.array(name, ...) + local rest = {...} + return name..table.concat(rest, '_') +end function bl.boolean(val) return val~=nil and val~=false and @@ -576,18 +638,62 @@ function bl.object(id) error('bl.object: id must be a ts object, number, or string', 2) end end -function bl.array(name, ...) - local rest = {...} - return name..table.concat(rest, '_') +function bl.string(val) + return valFromTsTostring(val) end -function _bllua_call(fnameS, ...) - local args = arglistFromTs('lua:'..fnameS:lower(), {...}) -- todo: allow this though bl.type - if not _G[fnameS] then - error('luacall: no global lua function named \''..fnameS..'\'') end - -- todo: library fields and object methods - local res = _G[fnameS](unpack(args)) + +-- Lua calling from TS +local luaLookup +luaLookup = function(tbl, name, set, val) + print('lookup', tbl, name, set, val) + if name:find('%.') then + local first, rest = name:match('^([^%.:]+)%.(.+)$') + if not isValidFuncName(first) then + error('luaLookup: invalid name \''..tostring(first)..'\'', 3) end + if not tbl[first] then + if set then tbl[first] = {} + else return nil end + end + return luaLookup(tbl[first], rest, set, val) + elseif name:find(':') then + local first, rest = name:match('^([^%.:]+):(.*)$') + if rest:find('[%.:]') then + error('luacall: cannot have : or . after :', 3) end + if not isValidFuncName(first) then + error('luacall: invalid name \''..tostring(first)..'\'', 3) end + if not tbl[first] then + error('luacall: no object named \''..rest..'\'', 3) end + if not tbl[first][rest] then + error('luacall: no method named \''..rest..'\'', 3) end + return function(...) + tbl[first][rest](tbl[first], ...) + end + else + if set then + tbl[name] = val + else + return tbl[name] + end + end +end +-- Todo: similar deep access for luaget and luaset +function _bllua_call(fname, ...) + local args = arglistFromTs(fname:lower(), {...}) -- todo: separate lua from ts func names? + local func = luaLookup(_G, fname) + if not func then + error('luacall: no global in lua named \''..name..'\'', 2) end + local res = func(unpack(args)) return valToTs(res) end +function _bllua_getvar(vname) + return valToTs(luaLookup(_G, vname)) +end +function _bllua_setvar(vname, valS) + return valToTs(luaLookup(_G, vname, true, valFromTs(valS, vname))) -- todo: separate lua from ts var names? +end +function _bllua_eval(code) return loadstring(code)() end +function _bllua_exec(fn) return dofile(fn, 2) end + -- bl.schedule: Use TS's schedule function to schedule lua calls -- bl.schedule(time, function, args...) diff --git a/src/util/libts-lua.lua b/src/util/libts-lua.lua index 96ef5fc..56f4a9d 100644 --- a/src/util/libts-lua.lua +++ b/src/util/libts-lua.lua @@ -192,12 +192,6 @@ function require(mod) return _bllua_requiresecure(mod) end --- Exposure to TS -function _bllua_getvar(name) return _G[name] end -function _bllua_setvar(name, val) _G[name] = val end -function _bllua_eval(code) return loadstring(code)() end -function _bllua_exec(fn) return dofile(fn, 2) end - local function isValidCode(code) local f,e = loadstring(code) return f~=nil diff --git a/src/util/libts-ts.cs b/src/util/libts-ts.cs index 292c40e..63e03a7 100644 --- a/src/util/libts-ts.cs +++ b/src/util/libts-ts.cs @@ -49,26 +49,4 @@ function _bllua_set_var(%name, %val) { return ""; } -// Public Lua library for TS -function luacall(%func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p) { - if($_bllua_active) - return _bllua_luacall("_bllua_call", %func, %a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p); -} -function luaexec(%fn) { - if($_bllua_active) - return _bllua_luacall("_bllua_exec", %fn); -} -function luaeval(%code) { - if($_bllua_active) - return _bllua_luacall("_bllua_eval", %code); -} -function luaget(%name) { - if($_bllua_active) - return _bllua_luacall("_bllua_getvar", %name); -} -function luaset(%name, %val) { - if($_bllua_active) - _bllua_luacall("_bllua_setvar", %name, %val); -} - echo(" Executed libts-ts.cs"); diff --git a/src/util/matrix.lua b/src/util/matrix.lua new file mode 100644 index 0000000..41a3542 --- /dev/null +++ b/src/util/matrix.lua @@ -0,0 +1,7 @@ + +-- todo +-- Matrix class with math operators + + + +print(' Executed matrix.lua') diff --git a/src/util/std.lua b/src/util/std.lua index f1041cf..ca79eea 100644 --- a/src/util/std.lua +++ b/src/util/std.lua @@ -179,7 +179,7 @@ valueToString = function(v, tabLevel, seen) return tostring(v) else --error('table.tostring: table contains a '..t..' value, cannot serialize') - return 'nil --[[ cannot serialize '..t..': '..tostring(v)..' ]]' + return 'nil --[[ cannot serialize '..tostring(v)..' ]]' end end function table.tostring(t) @@ -345,3 +345,6 @@ end function math.clamp(v, n, x) return math.min(x, math.max(v, n)) end + + +print(' Executed std.lua') diff --git a/src/util/vector.lua b/src/util/vector.lua index f166719..bc92791 100644 --- a/src/util/vector.lua +++ b/src/util/vector.lua @@ -216,4 +216,7 @@ vector_new = function(vi) end vector = vector_new + +print(' Executed vector.lua') + return vector_new