rework hooks, proper arg conversion for luacall from ts, fix+rename bl.addServerCmd/addClientCmd, remove 'bool' from ts.type (only use 'boolean'), more dedefault typedefs, support typedefs on named objects, typedef inheritance, reorganize libts

This commit is contained in:
Redo
2025-10-04 00:49:19 -05:00
parent 9c349a9352
commit eaafb42317
9 changed files with 1005 additions and 906 deletions

Binary file not shown.

View File

@@ -40,24 +40,36 @@ Lua scripting for Blockland
### Advanced ### Advanced
`sched = bl.schedule(timeMs, function, args...)` - Schedule a Lua function to be called later, similar to schedule in Torque `sched = bl.schedule(timeMs, function, args...)` - Schedule a Lua function to be called later, similar to schedule in Torque
`sched:cancel()` - Cancel a previously scheduled timer `sched:cancel()` - Cancel a previously scheduled timer
### Raycasts and Searches
`hitObject, hitPos, hitNormal = bl.raycast(vector{startPosX,y,z}, vector{endPosX,y,z}, 'objtype'/{'objtypes',...}, ignoreObjects...?)` - Cast a ray in the world over objects of the specified type(s) (possibly excluding some objects), and return the object hit, the position of the hit, and the normal vector to the surface hit. See the Types section for a list of valid object types. `hitObject, hitPos, hitNormal = bl.raycast(vector{startPosX,y,z}, vector{endPosX,y,z}, 'objtype'/{'objtypes',...}, ignoreObjects...?)` - Cast a ray in the world over objects of the specified type(s) (possibly excluding some objects), and return the object hit, the position of the hit, and the normal vector to the surface hit. See the Types section for a list of valid object types.
`for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types. `for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types.
`for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types. `for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types.
`bl.serverCmd('commandName', function(client, args...) code() end)` - Register a /-command on the server
`bl.clientCmd('commandName', function(args...) code() end)` - Register a client command on the client ### Server-Client Communication
`bl.addServerCmd('commandName', function(client, args...) code() end)` - Register a /-command on the server
`bl.addClientCmd('commandName', function(args...) code() end)` - Register a client command on the client
`bl.commandToServer('commandName', args...)` - Execute a server command as a client
`bl.commandToClient('commandName', args...)` - As the server, execute a client command on a specific client
`bl.commandToAll('commandName', args...)` - As the server, execute a client command on all clients
### Packages/Hooks ### Packages/Hooks
`bl.hook('packageName', 'functionName', 'before'/'after'/'override', function(args...) code() end)` - Hook a Torque function with a Lua function `bl.hook('packageName', 'functionName', 'before'/'after', function(args) code() end)` - Hook a Torque function with a Lua function.
`bl.unhook('packageName', 'functionName', 'before'/'after'/'override')` - Remove a previously defined hook `args` is an array containing the arguments provided to the function. If the hook is `before`, these can be modified before being passed to the parent function.
If `args._return` is set to anything other than nil by a `before` hook, the parent function will not be called, and the function will simply return that value. Also in this case, any `after` hook will not be executed.
In an `after` hook, `args._return` is set to the value returned by the parent function, and can be modified.
`bl.unhook('packageName', 'functionName', 'before'/'after')` - Remove a previously defined hook
`bl.unhook('packageName', 'functionName')` - Remove any previously defined hooks on the function within the package
`bl.unhook('packageName')` - Remove any previously defined hooks within the package
### Classes and Types ### Classes and Types
`bl.bool(thing)` - Convert a Torque boolean (0 or 1) into a Lua boolean. Done automatically for all built-in functions that return bools. `bl.type('varName', 'type')` - Register the type of a Torque global variable, for conversion when accessing from Lua. Valid types are 'boolean', 'object', and nil (default is nil, which applies automatic conversion).
`bl.object(thing)` - Convert a Torque object reference (object ID or name) into a Lua object. Done automatically for all built-in functions that return objects. `bl.type('funcName', 'type')` - Register the return type of a Torque function, for conversion when calling from Lua. Valid types are 'bool', 'object', and nil - all other conversion is automatic. Already done for all default functions.
`bl.type('varName', 'type')` - Register the type of a Torque global variable, for conversion when accessing from Lua. Valid types are 'bool', 'object', and nil - all other conversion is automatic.
`bl.type('funcName', 'type')` - Register the return type of a Torque function, for conversion when calling from Lua. Valid types are 'bool', 'object', and nil - all other conversion is automatic. Already done for all built-in functions that return objects.
`bl.type('className::funcName', 'type')` - Register the return type of a Torque object method. `bl.type('className::funcName', 'type')` - Register the return type of a Torque object method.
`bl.class('className')` - Register a Torque class to be used from Lua (Already done for all built-in classes) `bl.class('className')` - Register a Torque class to be used from Lua (Already done for all built-in classes)
`bl.class('className', 'parentClassName')` - Same as above, with inheritance `bl.class('className', 'parentClassName')` - Same as above, with inheritance
`bl.bool(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.
### File I/O ### File I/O
Lua's builtin file I/O is emulated, and is confined to the same directories as TorqueScript file I/O. Lua's builtin file I/O is emulated, and is confined to the same directories as TorqueScript file I/O.
@@ -95,7 +107,7 @@ Like in standard Lua, modules loaded using `require` are only executed the first
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. 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. 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 ### From TorqueScript to Lua
- Any numeric value becomes a Lua `number`, except as specified with `ts.type`, which may convert a value into a `boolean` or a Torque object container. - 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` - The empty string "" becomes `nil`
- A string containing three numbers separated by spaces becomes a `vector` - 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 - A string containing six numbers separated by spaces becomes a table of two vectors

View File

@@ -28,8 +28,8 @@ INCLUDE_BIN(bll_fileLuaEnv , "lua-env.lua");
INCLUDE_BIN(bll_fileTsEnv , "ts-env.cs" ); INCLUDE_BIN(bll_fileTsEnv , "ts-env.cs" );
INCLUDE_BIN(bll_fileLuaStd , "util/std.lua"); INCLUDE_BIN(bll_fileLuaStd , "util/std.lua");
INCLUDE_BIN(bll_fileLuaVector , "util/vector.lua"); INCLUDE_BIN(bll_fileLuaVector , "util/vector.lua");
INCLUDE_BIN(bll_fileLuaLibts , "util/libts.lua"); INCLUDE_BIN(bll_fileLuaLibts , "util/libts-lua.lua");
INCLUDE_BIN(bll_fileTsLibtsSupport, "util/libts-support.cs"); INCLUDE_BIN(bll_fileTsLibts , "util/libts-ts.cs");
INCLUDE_BIN(bll_fileLuaLibbl , "util/libbl.lua" ); INCLUDE_BIN(bll_fileLuaLibbl , "util/libbl.lua" );
INCLUDE_BIN(bll_fileLuaLibblTypes , "util/libbl-types.lua"); INCLUDE_BIN(bll_fileLuaLibblTypes , "util/libbl-types.lua");
INCLUDE_BIN(bll_fileTsLibblSupport, "util/libbl-support.cs"); INCLUDE_BIN(bll_fileTsLibblSupport, "util/libbl-support.cs");
@@ -68,7 +68,7 @@ bool init() {
BLL_LOAD_LUA(gL, bll_fileLuaStd); BLL_LOAD_LUA(gL, bll_fileLuaStd);
BLL_LOAD_LUA(gL, bll_fileLuaVector); BLL_LOAD_LUA(gL, bll_fileLuaVector);
BLL_LOAD_LUA(gL, bll_fileLuaLibts); BLL_LOAD_LUA(gL, bll_fileLuaLibts);
BlEval(bll_fileTsLibtsSupport); BlEval(bll_fileTsLibts);
BLL_LOAD_LUA(gL, bll_fileLuaLibbl); BLL_LOAD_LUA(gL, bll_fileLuaLibbl);
BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes); BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes);
BlEval(bll_fileTsLibblSupport); BlEval(bll_fileTsLibblSupport);

View File

@@ -1,26 +0,0 @@
// Built-in functions
// Eval'd after BLLua4 has loaded the Lua environment and API
// 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(%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 bllua-env.cs");

View File

@@ -10,7 +10,7 @@ package _bllua_smartEval {
if($_bllua_active) { if($_bllua_active) {
%text = getSubStr(%text, 1, strLen(%text)); %text = getSubStr(%text, 1, strLen(%text));
echo("Lua ==> " @ %text); echo("Lua ==> " @ %text);
luacall("_bllua_smarteval", %text); _bllua_luacall("_bllua_smarteval", %text);
} else { } else {
echo("Lua: not loaded"); echo("Lua: not loaded");
} }
@@ -36,7 +36,8 @@ package _bllua_objectDeletionHook {
// note: no parent function exists by default, // note: no parent function exists by default,
// and this is loaded before any addons // and this is loaded before any addons
//parent::onRemove(%obj); //parent::onRemove(%obj);
if($_bllua_active) luacall("_bllua_objectDeleted", %obj); // assuming obj is an ID and never a name
if($_bllua_active) _bllua_luacall("_bllua_objectDeleted", %obj);
} }
}; };
activatePackage(_bllua_objectDeletionHook); activatePackage(_bllua_objectDeletionHook);

File diff suppressed because it is too large Load Diff

View File

@@ -106,21 +106,29 @@ local function valToTs(val)
error('valToTs: could not convert '..type(val), 3) error('valToTs: could not convert '..type(val), 3)
end end
end end
local function convertValFromTs(val, typ)
if typ=='boolean' then
return tsBool(val)
elseif typ=='object' then
return toTsObject(val)
else
error('valFromTs: invalid force type '..typ, 4)
end
end
bl._forceType = bl._forceType or {} bl._forceType = bl._forceType or {}
local function valFromTs(val, name) 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
local nameL = name:lower() name = name:lower()
if bl._forceType[nameL] then if bl._forceType[name] then
local typ = bl._forceType[nameL] return convertValFromTs(val, bl._forceType[name])
if typ=='boolean' then end
return tsBool(val) end
elseif typ=='object' then if name2 then
return toTsObject(val) name2 = name2:lower()
else if bl._forceType[name2] then
error('valFromTs: invalid force type '..typ, 3) return convertValFromTs(val, bl._forceType[name2])
end
end end
end end
-- '' -> nil -- '' -> nil
@@ -151,22 +159,35 @@ end
local function arglistToTs(args) local function arglistToTs(args)
return map(args, valToTs) return map(args, valToTs)
end end
function bl.type(name, typ) local function classFromForceTypeStr(name)
if typ~='bool' and typ~='boolean' and typ~='object' and typ~=nil then local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$')
error('bl.type: can only set type to \'bool\' or \'object\' or nil', 2) end if not class then
if not isValidFuncNameNsArgn(name) then class, rest = name:match('^([a-zA-Z0-9_]+)(%..+)$') end
error('bl.type: invalid function or variable name \''..name..'\'', 2) end return class,rest
if typ=='bool' then typ='boolean' end
bl._forceType[name:lower()] = typ
-- apply to children (wip)
--local class, rest = name:match('^([a-zA-Z0-9_]+)(::.+)$')
--if not class then
-- class, rest = name:match('^([a-zA-Z0-9_]+)(%..+)$') end
--if class then
--
--end
end end
local setForceType
setForceType = function(ftname, typ)
if typ~='boolean' and typ~='object' and typ~=nil then
error('bl.type: can only set type to \'boolean\', \'object\', or nil', 2) end
if not isValidFuncNameNsArgn(ftname) then
error('bl.type: invalid function or variable name \''..ftname..'\'', 2) end
ftname = ftname:lower()
bl._forceType[ftname] = typ
-- apply to child classes if present
local cname, rest = classFromForceTypeStr(ftname)
if cname then
local meta = bl._objectUserMetas[cname]
if meta then
for chcname,_ in pairs(meta._children) do
setForceType(chcname..rest, typ)
end
end
end
end
bl.type = setForceType
-- Value detection -- Value detection
@@ -210,40 +231,47 @@ end
local tsClassMeta = { local tsClassMeta = {
__tostring = function(t) __tostring = function(t)
return 'torqueClass:'..t._name.. return 'torqueClass:'..t._name..
(t._inherit and (':'..t._inherit) or '') (t._inherit and (':'..t._inherit._name) or '')
end, end,
} }
bl._objectUserMetas = bl._objectUserMetas or {} bl._objectUserMetas = bl._objectUserMetas or {}
function bl.class(name, inherit) function bl.class(cname, inhname)
if not ( type(name)=='string' and isValidFuncName(name) ) 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 ( inherit==nil or (type(inherit)=='string' and isValidFuncName(inherit)) ) 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
name = name:lower() cname = cname:lower()
local met = bl._objectUserMetas[name] or { local met = bl._objectUserMetas[cname] or {
_name = name, _name = cname,
_inherit = nil, _inherit = nil,
_children = {}, _children = {},
} }
bl._objectUserMetas[name] = met bl._objectUserMetas[cname] = met
setmetatable(met, tsClassMeta) setmetatable(met, tsClassMeta)
if inherit then if inhname then
inherit = inherit:lower() inhname = inhname:lower()
local inh = bl._objectUserMetas[inherit] local inh = bl._objectUserMetas[inhname]
if not inh then error('bl.class: argument #2: \''..inherit..'\' is not the '.. if not inh then error('bl.class: argument #2: \''..inhname..'\' is not the '..
'name of an existing class', 2) end 'name of an existing class', 2) end
inh._children[name] = 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
for ftname, typ in pairs(bl._forceType) do
local cname2, rest = classFromForceTypeStr(ftname)
if cname2==inhname then
setForceType(cname..rest, typ)
end
end
end end
end end
local function objectInheritedMetas(name) local function objectInheritedMetas(name)
@@ -279,11 +307,15 @@ local tsObjectMeta = {
return function(t, ...) return function(t, ...)
local args = {...} local args = {...}
local argsS = arglistToTs(args) local argsS = arglistToTs(args)
return valFromTs(_bllua_ts.callobj(rawget(t,'_tsObjectId'), name, unpack(argsS)), return valFromTs(
_bllua_ts.callobj(rawget(t,'_tsObjectId'), name, unpack(argsS)),
rawget(t,'_tsName') and rawget(t,'_tsName')..'::'..name,
rawget(t,'_tsNamespace')..'::'..name) rawget(t,'_tsNamespace')..'::'..name)
end end
else else
return valFromTs(_bllua_ts.getfield(rawget(t,'_tsObjectId'), name), return valFromTs(
_bllua_ts.getfield(rawget(t,'_tsObjectId'), name),
rawget(t,'_tsName') and rawget(t,'_tsName')..'.'..name,
rawget(t,'_tsNamespace')..'.'..name) rawget(t,'_tsNamespace')..'.'..name)
end end
end end
@@ -489,7 +521,7 @@ local tsMeta = {
-- bl.set(name, value) -- bl.set(name, value)
-- Used to set global variables -- Used to set global variables
function bl.set(name, val) function bl.set(name, val)
_bllua_ts.call('_bllua_set_var', name, valToTs(val)) _bllua_ts.setvar(name, valToTs(val))
end end
-- Utility functions -- Utility functions
@@ -499,7 +531,7 @@ function bl.call(func, ...)
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.call('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))
@@ -524,8 +556,13 @@ function bl.array(name, ...)
local rest = {...} local rest = {...}
return name..table.concat(rest, '_') return name..table.concat(rest, '_')
end end
function _bllua_call(name, ...) function _bllua_call(fnameS, ...)
-- todo: call ts->lua using this instead of directly local args = arglistFromTs(fnameS:lower(), {...})
if not _G[fnameS] then
error('luacall: no global lua function named \''..fnameS..'\'') end
-- todo: library fields and object methods
local res = _G[fnameS](args)
return valToTs(res)
end end
-- bl.schedule: Use TS's schedule function to schedule lua calls -- bl.schedule: Use TS's schedule function to schedule lua calls
@@ -544,7 +581,7 @@ function bl.schedule(time, cb, ...)
bl._scheduleNextId = bl._scheduleNextId+1 bl._scheduleNextId = bl._scheduleNextId+1
local args = {...} local args = {...}
local handle = tonumber(_bllua_ts.call('schedule', local handle = tonumber(_bllua_ts.call('schedule',
time, 0, 'luacall', '_bllua_schedule_callback', id)) time, 0, '_bllua_luacall', '_bllua_schedule_callback', id))
local sch = { local sch = {
callback = cb, callback = cb,
args = args, args = args,
@@ -554,8 +591,9 @@ function bl.schedule(time, cb, ...)
bl._scheduleTable[id] = sch bl._scheduleTable[id] = sch
return sch return sch
end end
function _bllua_schedule_callback(id) function _bllua_schedule_callback(idS)
id = tonumber(id) or error('_bllua_schedule_callback: invalid id: '..tostring(id)) local id = tonumber(idS) or
error('_bllua_schedule_callback: invalid id: '..tostring(idS))
local sch = bl._scheduleTable[id] local sch = bl._scheduleTable[id]
if not sch then error('_bllua_schedule_callback: no schedule with id '..id) end if not sch then error('_bllua_schedule_callback: no schedule with id '..id) end
bl._scheduleTable[id] = nil bl._scheduleTable[id] = nil
@@ -563,26 +601,48 @@ function _bllua_schedule_callback(id)
end end
-- serverCmd and clientCmd -- serverCmd and clientCmd
-- bl.serverCmd('suicide', function(client) client.player:kill() end)
bl._cmds = bl._cmds or {} bl._cmds = bl._cmds or {}
function _bllua_process_cmd(cmdS, clientS, ...) function _bllua_process_cmd(cmdS, ...)
local client = toTsObject(clientS) local cmd = cmdS:lower()
local argsS = {...} local args = arglistFromTs(cmd, {...})
local args = arglistFromTs(cmdS, argsS) local func = bl._cmds[cmd]
local func = bl._cmds[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
pcall(func, client, 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._servercmds[cmd] = func bl._cmds[cmd] = func
local arglist = '%a,%b,%c,%d,%e,%f,%g,%h' local arglist = '%a,%b,%c,%d,%e,%f,%g,%h,%i,%j,%k,%l,%m,%n,%o,%p'
_bllua_ts.eval('function '..cmd..'(%cl,'..arglist..'){'.. _bllua_ts.eval('function '..cmd..'('..arglist..'){'..
'luacall(_bllua_process_cmd,"'..cmd..'",%cl,'..arglist..');}') '_bllua_luacall(_bllua_process_cmd,"'..cmd..'",'..arglist..');}')
end
function bl.addServerCmd(name, func)
name = name:lower()
addCmd('servercmd'..name, func)
bl._forceType['servercmd'..name..':1'] = 'object'
end
function bl.addClientCmd(name, func)
name = name:lower()
addCmd('clientcmd'..name, func)
end
-- commandToServer and commandToClient
function bl.commandToServer(cmd, ...)
_bllua_ts.call('commandToServer',
_bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({...})))
end
function bl.commandToClient(cmd, ...)
_bllua_ts.call('commandToClient',
_bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({...})))
end
function bl.commandToAll(cmd, ...)
_bllua_ts.call('commandToAll',
_bllua_ts.call('addTaggedString', cmd),
unpack(arglistToTs({...})))
end end
function bl.serverCmd(name, func) addCmd('serverCmd'..name, func) end
function bl.clientCmd(name, func) addCmd('clientCmd'..name, func) end
-- Hooks (using TS packages) -- Hooks (using TS packages)
local function isPackageActive(pkg) local function isPackageActive(pkg)
@@ -603,42 +663,67 @@ local function deactivatePackage(pkg)
_bllua_ts.call('deactivatePackage', pkg) _bllua_ts.call('deactivatePackage', pkg)
end end
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 {} bl._hooks = bl._hooks or {}
function _bllua_process_hook(pkgS, nameS, timeS, ...) function _bllua_process_hook_before(pkgS, nameS, ...)
local argsS = {...} local args = arglistFromTs(nameS, {...})
local args = arglistFromTs(nameS, argsS)
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][timeS] bl._hooks[pkgS][nameS].before
if not func then if not func then
error('_bllua_process_hook: no hook for '..pkgS..':'..nameS..':'..timeS) end error('_bllua_process_hook_before: no hook for '..pkgs..':'..nameS) end
_bllua_ts.setvar('_bllua_hook_abort', '0')
pcall(func, args) func(args) --pcall(func, args)
if args._return then
_bllua_ts.setvar('_bllua_hook_abort', '1')
_bllua_ts.setvar('_bllua_hook_return', valToTs(args._return))
end
for i=1,hookNargs do
_bllua_ts.setvar('_bllua_hook_arg'..i, valToTs(args[i]))
end
end
function _bllua_process_hook_after(pkgS, nameS, resultS, ...)
nameS = nameS:lower()
local args = arglistFromTs(nameS, {...})
args._return = valFromTs(resultS, nameS)
local func = bl._hooks[pkgS] and bl._hooks[pkgS][nameS] and
bl._hooks[pkgS][nameS].after
if not func then
error('_bllua_process_hook_after: no hook for '..pkgs..':'..nameS) end
func(args) --pcall(func, args)
return valToTs(args._return)
end end
local function updateHook(pkg, name, hk) local function updateHook(pkg, name, hk)
local arglist = '%a,%b,%c,%d,%e,%f,%g,%h'
local beforeCode = hk.before and local beforeCode = hk.before and
('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. ('_bllua_luacall("_bllua_process_hook_before", "'..pkg..'","'..name..
'", "before", '..arglist..');') or '' '",'..hookArglistLocal..');') or ''
local parentCode = hk.override and local arglist = (hk.before and hookArglistGlobal or hookArglistLocal)
('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. local parentCode =
'", "override", '..arglist..');') or tsIsFunctionNsname(name) and -- only call parent if it exists
(tsIsFunctionNsname(name) and (hk.before and
('parent::'..name:match('[^:]+$')..'('..arglist..');') or '') 'if($_bllua_hook_abort)return $_bllua_hook_return; else ' or '')..
((hk.after and '%result=' or 'return ')..
'parent::'..name:match('[^:]+$')..
'('..arglist..');') or ''
local afterCode = hk.after and local afterCode = hk.after and
('luacall("_bllua_process_hook", "'..pkg..'", "'..name.. ('return _bllua_luacall("_bllua_process_hook_after","'..pkg..'","'..name..'",%result,'..
'", "after", '..arglist..');') or '' arglist..');') or ''
bl.eval('package '..pkg..'{function '..name..'('..arglist..'){'.. local code =
beforeCode..parentCode..afterCode..'}};') 'package '..pkg..'{'..
'function '..name..'('..hookArglistLocal..'){'..
beforeCode..parentCode..afterCode..
'}'..
'};'
_bllua_ts.eval(code)
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' and time~='override' then if time~='before' and time~='after' then
error('bl.hook: argument #3: time must be one of '.. error('bl.hook: argument #3: time must be \'before\' or \'after\'', 2) end
'\'before\' \'after\' \'override\'', 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
@@ -654,9 +739,8 @@ function bl.unhook(pkg, name, time)
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' and time~='override' then if time~='before' and time~='after' then
error('bl.unhook: argument #3: time must be one of '.. error('bl.unhook: argument #3: time must be \'before\' or \'after\'', 2) end
'\'before\' \'after\' \'override\'', 2) end
if not name then if not name then
if bl._hooks[pkg] then if bl._hooks[pkg] then
@@ -679,15 +763,16 @@ function bl.unhook(pkg, name, time)
end end
updateHook(pkg, name, {}) updateHook(pkg, name, {})
else else
if time~='before' and time~='after' and time~='override' then if time~='before' and time~='after' then
error('bl.unhook: argument #3: time must be nil or one of '.. error('bl.unhook: argument #3: time must be nil, \'before\', or \'after\'', 2) end
'\'before\' \'after\' \'override\'', 2) end
bl._hooks[pkg][name][time] = nil bl._hooks[pkg][name][time] = nil
if table.empty(bl._hooks[pkg][name]) and table.empty(bl._hooks[pkg]) then if table.empty(bl._hooks[pkg][name]) and table.empty(bl._hooks[pkg]) then
bl._hooks[pkg] = nil bl._hooks[pkg] = nil
deactivatePackage(pkg) deactivatePackage(pkg)
updateHook(pkg, name, {})
else
updateHook(pkg, name, bl._hooks[pkg][name])
end end
updateHook(pkg, name, bl._hooks[pkg][name])
end end
else else
--error('bl.unhook: no hooks registered for function \''..name.. --error('bl.unhook: no hooks registered for function \''..name..

View File

@@ -213,4 +213,8 @@ function _bllua_smarteval(code)
end end
end end
_bllua_ts.call('echo', ' Executed libts.lua') function ts.setvar(name, val)
_bllua_ts.call('_bllua_set_var', name, val)
end
_bllua_ts.call('echo', ' Executed libts-lua.lua')

View File

@@ -49,4 +49,26 @@ function _bllua_set_var(%name, %val) {
return ""; return "";
} }
echo(" Executed libts-support.cs"); // 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");