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:
		
							
								
								
									
										220
									
								
								src/util/libts-lua.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/util/libts-lua.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
|  | ||||
| -- 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 only has access to the sandboxed lua environment, just like user code. | ||||
|  | ||||
| ts = _bllua_ts | ||||
|  | ||||
| -- Provide limited OS functions | ||||
| os = os or {} | ||||
| function os.time() return math.floor(tonumber(_bllua_ts.call('getSimTime'))/1000) end | ||||
| function os.clock() return tonumber(_bllua_ts.call('getSimTime'))/1000 end | ||||
|  | ||||
| -- Virtual file class, emulating a file object as returned by io.open | ||||
| -- Used to wrap io.open to allow reading from zips (using TS) | ||||
| -- Not perfect because TS file I/O sucks | ||||
| --   Can't read nulls, can't distinguish between CRLF and LF. | ||||
| -- Todo someday: actually read the zip in lua? | ||||
| local file_meta = { | ||||
| 	read = function(file, mode) | ||||
| 		file:_init() | ||||
| 		if not file or type(file)~='table' or not file._is_file then error('File:read: Not a file', 2) end | ||||
| 		if file._is_open ~= true then error('File:read: File is closed', 2) end | ||||
| 		if mode=='*n' then | ||||
| 			local ws, n = file.data:match('^([ \t\r\n]*)([0-9%.%-e]+)', file.pos) | ||||
| 			if n then | ||||
| 				file.pos = file.pos + #ws + #n | ||||
| 				return n | ||||
| 			else | ||||
| 				return nil | ||||
| 			end | ||||
| 		elseif mode=='*a' then | ||||
| 			local d = file.data:sub(file.pos, #file.data) | ||||
| 			file.pos = #file.data + 1 | ||||
| 			return d | ||||
| 		elseif mode=='*l' then | ||||
| 			local l, ws = file.data:match('^([^\r\n]*)(\r?\n)', file.pos) | ||||
| 			if not l then | ||||
| 				l = file.data:match('^([^\r\n]*)$', file.pos); ws = ''; | ||||
| 				if l=='' then return nil end | ||||
| 			end | ||||
| 			if l then | ||||
| 				file.pos = file.pos + #l + #ws | ||||
| 				return l | ||||
| 			else | ||||
| 				return nil | ||||
| 			end | ||||
| 		elseif type(mode)=='number' then | ||||
| 			local d = file.data:sub(file.pos, file.pos+mode) | ||||
| 			file.pos = file.pos + #d | ||||
| 			return d | ||||
| 		else | ||||
| 			error('File:read: Invalid mode \''..mode..'\'', 2) | ||||
| 		end | ||||
| 	end, | ||||
| 	lines = function(file) | ||||
| 		file:_init() | ||||
| 		return function() | ||||
| 			return file:read('*l') | ||||
| 		end | ||||
| 	end, | ||||
| 	close = function(file) | ||||
| 		if not file._is_open then error('File:close: File is not open', 2) end | ||||
| 		file._is_open = false | ||||
| 	end, | ||||
| 	__index = function(f, k) return rawget(f, k) or getmetatable(f)[k] end, | ||||
| 	_init = function(f) | ||||
| 		if not f.data then | ||||
| 			f.data = _bllua_ts.call('_bllua_ReadEntireFile', f.filename) | ||||
| 		end | ||||
| 	end, | ||||
| } | ||||
| local function new_file_obj(fn) | ||||
| 	local file = { | ||||
| 		_is_file = true, | ||||
| 		_is_open = true, | ||||
| 		pos = 1, | ||||
| 		__index = file_meta.__index, | ||||
| 		filename = fn, | ||||
| 		data = nil, | ||||
| 	} | ||||
| 	setmetatable(file, file_meta) | ||||
| 	return file | ||||
| 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{ | ||||
| 	'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders' | ||||
| } | ||||
| local function io_open_absolute(fn, mode) | ||||
| 	-- if file exists, use original mode | ||||
| 	local res, err = _bllua_io_open(fn, mode) | ||||
| 	if res then return res end | ||||
| 	 | ||||
| 	-- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader | ||||
| 	local dir = fn:match('^[^/]+') | ||||
| 	if not allowed_zip_dirs[dir:lower()] then return nil, 'File is not in one of the allowed directories' end | ||||
| 	local exist = _bllua_ts.call('isFile', fn) == '1' | ||||
| 	if not exist then return nil, err end | ||||
| 	 | ||||
| 	if mode~=nil and mode~='r' and mode~='rb' then | ||||
| 		return nil, 'Files in zips can only be opened in read mode' end | ||||
| 	 | ||||
| 	-- return a temp lua file object with the data | ||||
| 	local fi = new_file_obj(fn) | ||||
| 	return fi | ||||
| end | ||||
|  | ||||
| io = io or {} | ||||
| function io.open(fn, mode, errn) | ||||
| 	errn = errn or 1 | ||||
| 	 | ||||
| 	-- try to open the file with relative path, otherwise use absolute path | ||||
| 	local curfn = debug.getfilename(errn + 1) or _bllua_ts.getvar('Con::File') | ||||
| 	if curfn == '' then curfn = nil end | ||||
| 	if fn:find('^%.') then | ||||
| 		local relfn = curfn and fn:find('^%./') and | ||||
| 			curfn:gsub('[^/]+$', '')..fn:gsub('^%./', '') | ||||
| 		if relfn then | ||||
| 			local fi, err = io_open_absolute(relfn, mode) | ||||
| 			return fi, err, relfn | ||||
| 		else | ||||
| 			return nil, 'Invalid path', fn | ||||
| 		end | ||||
| 	else | ||||
| 		local fi, err = io_open_absolute(fn, mode) | ||||
| 		return fi, err, fn | ||||
| 	end | ||||
| end | ||||
| function io.lines(fn) | ||||
| 	local fi, err, fn2 = io.open(fn, nil, 2) | ||||
| 	if not fi then error('Error opening file \''..fn2..'\': '..err, 2) end | ||||
| 	return fi:lines() | ||||
| end | ||||
| function io.type(f) | ||||
| 	if type(f)=='table' and f._is_file then | ||||
| 		return f._is_open and 'file' or 'closed file' | ||||
| 	else | ||||
| 		return _bllua_io_type(f) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| -- provide dofile | ||||
| function dofile(fn, errn) | ||||
| 	errn = errn or 1 | ||||
| 	 | ||||
| 	local fi, err, fn2 = io.open(fn, 'r', errn+1) | ||||
| 	if not fi then error('Error executing file \''..fn2..'\': '..err, errn+1) end | ||||
| 	 | ||||
| 	print('Executing '..fn2) | ||||
| 	local text = fi:read('*a') | ||||
| 	fi:close() | ||||
| 	return assert(loadstring('--[['..fn2..']]'..text))() | ||||
| end | ||||
|  | ||||
| -- provide require (just a wrapper for dofile) | ||||
| -- searches for ?.lua and ?/init.lua in the following directories: | ||||
| --   location of current file | ||||
| --   blockland directory | ||||
| --   current add-on | ||||
| local function file_exists(fn, errn) | ||||
| 	local fi, err, fn2 = io.open(fn, 'r', errn+1) | ||||
| 	if fi then | ||||
| 		fi:close() | ||||
| 		return fn2 | ||||
| 	else | ||||
| 		return nil | ||||
| 	end | ||||
| end | ||||
| local require_memo = {} | ||||
| function require(mod) | ||||
| 	if require_memo[mod] then return unpack(require_memo[mod]) end | ||||
| 	local fp = mod:gsub('%.', '/') | ||||
| 	local fns = { | ||||
| 		'./'..fp..'.lua',      -- local file | ||||
| 		'./'..fp..'/init.lua', -- local library | ||||
| 		fp..'.lua',            -- global file | ||||
| 		fp..'/init.lua',       -- global library | ||||
| 	} | ||||
| 	if fp:lower():find('^add-ons/') then | ||||
| 		local addonpath = fp:lower():match('^add-ons/[^/]+')..'/' | ||||
| 		table.insert(fns, addonpath..fp..'.lua')      -- add-on file | ||||
| 		table.insert(fns, addonpath..fp..'/init.lua') -- add-on library | ||||
| 	end | ||||
| 	for _,fn in ipairs(fns) do | ||||
| 		local fne = file_exists(fn, 2) | ||||
| 		if fne then | ||||
| 			local res = {dofile(fne, 2)} | ||||
| 			require_memo[mod] = res | ||||
| 			return unpack(res) | ||||
| 		end | ||||
| 	end | ||||
| 	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 | ||||
| end | ||||
| function _bllua_smarteval(code) | ||||
| 	if (not code:find('^print%(')) and isValidCode('print('..code..')') then | ||||
| 		code = 'print('..code..')' end | ||||
| 	local f,e = loadstring(code) | ||||
| 	if f then | ||||
| 		return f() | ||||
| 	else | ||||
| 		print(e) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function ts.setvar(name, val) | ||||
| 	_bllua_ts.call('_bllua_set_var', name, val) | ||||
| end | ||||
|  | ||||
| _bllua_ts.call('echo', '  Executed libts-lua.lua') | ||||
		Reference in New Issue
	
	Block a user
	 Redo
					Redo