Lua: Help function: Can I extract the name of a function? - lua
I would like to learn by developing a help function. The code below outlines my plan, and it can be summarized as:
For functions defined in Lua files - look for comments in the source.
For built-in functions and DLLs - look for a text file in .\help\function.txt.
For libraries (if no comments in the source) - look for a text file in .\lib\help\function.txt.
At the end of my code you can see an attempt to build an index of function names using their unique tostring(function) return value. Surely I should be able to do this in a loop?
function learn()
-- learn()
-- Make it easier to learn Lua, based upon Matlab console functions
-- help() print for help text in function source or help directory
-- who() print defined variables (exlcuding those in loaded modules)
-- what() print defined functions (exlcuding those in loaded modules)
-- which() print path to function source file if present
-- list() print the file to the console
-- edit() edit "filename" or function source if present
-- note: edit(_) can be used after any call to list(), help(func),
-- Helper functions
-- table.name() returns table name as string
-- table.length() this is difficult
-- table.keylist() returns a list of keys
-- table.keytype() returns a list of key types
-- edit_source() process function names
-- edit_new() create new "filename" (will use but not make subdirectories)
-- string.split() returns a table from a string
-- io.exists() test if a filename exists
-- io.newfile() creates an empty file
--
-- global variables
-- editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
-- helpindex a list of the names of the inbuilt functions - see end of file
-- topics a table of help topics see topics.lua
-- web = "web"
web = "web"
-- webhelp = "http://www.nongnu.org/gsl-shell/doc/"
webhelp = "http://www.nongnu.org/gsl-shell/doc/"
-- editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
-- required packages
-- lfs - lua file system (binary from lua-files)
require("lfs")
-- topics - for the help system
require("topics")
end
learn()
function who(t,i)
-- who(table)
-- searches the table (or defaults to _G) to print a list of table keys + types
-- the second parameter is to add a prefix for a recursive call to search "sub" tables
-- still having difficulty with the "loaded" table as this is self referencing and sets up an infinate loop!
-- designed for the console, but could be modified to return a table
--
if type(t)~="table" then
t=_G
end
if type(i)~="string" then
i=""
end
local s={}
local u={}
s = table.keylist(t)
u = table.keytype(t)
for k,v in ipairs(s) do
if u[k]=="table" and s[k]~="_G" and s[k]~="loaded" then
who(t[s[k]],i..v..".")
else
if u[k]~="table" and u[k]~="function" and u[k]~="cdata" then
print(u[k], i..v)
end
end
end
end
function what(t,i)
-- what(table)
-- searches the table (or defaults to _G) to print a list of function names
-- the second parameter is to add a prefix for a recursive call to search "sub" tables
-- still having difficulty with the "loaded" table as this is self referencing and sets up an infinate loop!
-- designed for the console, but could be modified to return a table
--
if type(t)~="table" then
t=_G
end
if type(i)~="string" then
i=""
end
local s={}
local u={}
s = table.keylist(t)
u = table.keytype(t)
for k,v in ipairs(s) do
if u[k]=="table" and s[k]~="_G" and s[k]~="loaded" then
what(t[s[k]],i..v..".")
else
if u[k]=="function" then
print(u[k], i..v)
end
end
end
end
function which(funcname)
-- which(funcname)
-- identifies the source for the current definition of funcname
-- designed for the console, but could be modified to return a string
--
if type(funcname)~="function" then return end
local filename = _G.debug.getinfo(funcname).short_src
if filename=="[C]" then
print(tostring(funcname))
else
return filename
end
end
function help(funcname)
-- help(object)
-- for functions prints help text (from source or help\function.txt)
-- adding help text to source as ^--comments is recommended,
-- for builtin functions use a subdirectory from the executable,
-- for uncommented source add a sibling help directory
-- for table prints table name, size and list of contents
-- for variables prints the type of the object
--
if type(funcname)=="boolean" then
io.write("boolean: ")
print(funcname)
return
end
if type(funcname)=="string" then
if funcname=="web" then
os.launch(webhelp)
else
print("string: "..funcname)
end
return
end
if type(funcname)=="number" then
print("number: "..funcname)
return
end
if type(funcname) == 'userdata' then
print(tostring(funcname))
io.write("metadata: ")
print(getmetatable(funcname))
end
if type(funcname) == 'cdata' then
print(tostring(funcname))
-- *** Unfinished
end
if type(funcname)=="table" then
print(tostring(funcname)..", size: "..table.length(funcname))
who(funcname)
what(funcname)
return
end
if type(funcname)=="function" then
-- Test for a source file
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local codestart = _G.debug.getinfo(funcname).linedefined
local codeend = _G.debug.getinfo(funcname).lastlinedefined
if codestart < 1 then
print("Start is less than 1")
codestart = 1
end
if codeend< 1 then
print("End is less than 1")
codeend= 100
end
-- Try to read comments from the source
local output = 0
local count = 0
for line in io.lines(filename) do
count = count+1
if count > codestart and count < codeend then
if line:match("^%-%-") then
print(line)
output = output + 1
end
end
end
if output>0 then
io.write("From : ")
return filename -- to be used with edit(_)
end
-- Test for a help file as a sibling of the source
if output==0 then
-- No comments in the source file so look for a help file
local t = string.split(filename, "\\")
local helppath = table.concat(t,"\\",1,table.length(t)-1).."\\help\\"..t[table.length(t)]
helppath = string.gsub(helppath, "%.lua$" , ".txt")
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
print("No help in source file : "..filename)
io.write("No help in: ")
return helppath -- to be used with edit_new(_)
end
end
end
-- Test for a help file in the generic help directory
if helpindex[tostring(funcname)] then
local helppath = "help\\"..helpindex[tostring(funcname)]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
io.write("Built in function, but no help in: ")
return helppath -- to be used with edit_new(_)
end
else
print("No help index entry for "..tostring(funcname))
return
end
end
end
function list(filename)
if type(filename)=="function" then
print("list will only accept a string with a valid file name")
return
end
if type(filename)~="string" then
print("list will only accept a string with a valid file name")
return
end
if io.exists(filename) then
for line in io.lines(filename) do
print(line)
end
return filename
else
io.write("Can't find file: ")
return filename
end
end
function edit(filename, linenum)
-- edit(filename[, linenum])
-- loads the file into my editor (defined as global editor)
-- the linenum parameter will move the cursor to linenum
-- you will need to edit the global "editor" and the source command line below
-- or download EditorĀ² from http://www.zabkat.com
--
if type(filename)=="function" then
filename = edit_source(filename)
return filename
end
if type(filename)~="string" then return end
if type(linenum)~="number" then linenum=1 end
if io.exists(filename) then
os.launch(editor.." /P /L:"..linenum.." \""..filename.."\"", " /P /L:"..linenum.." \""..filename.."\"")
else
print("To make a new file edit_new('filename')")
io.write("Can't find file: ")
return filename
end
end
function edit_source(funcname)
if type(funcname)~="function" then return end
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local linenum = _G.debug.getinfo(funcname).linedefined
if linenum < 1 then
linenum = 1
end
edit(filename, linenum)
io.write("Editing : ")
return filename
end
end
function edit_new(filename)
if type(filename)~="string" then return end
io.newfile(filename)
edit(filename) -- This will check for a valid file name
io.write("Editing : ")
return filename
end
function table.name(table)
if type(table)~="table" then return end
for k, v in pairs(_G) do
if v == table then
return k
end
end
return nil
end
function table.length(table)
if type(table)~="table" then return end
local len = 0
for _ in pairs(table) do
len = len + 1
end
if type(len)=="number" then
return len
else
return nil
end
end
function table.keylist(table)
if type(table)~="table" then return end
local keylist={}
local n=0
for key in pairs(table) do
n=n+1
keylist[n]=key
end
return keylist
end
function table.keytype(table)
if type(table)~="table" then return end
local keytype={}
local n=0
for key in pairs(table) do
n=n+1
keytype[n]=type(table[key])
end
return keytype
end
function table.tablelist(table)
if type(table)~="table" then return end
local tablelist={}
local n=0
for key in pairs(table) do
if type(table[key])=="table" then
n=n+1
tablelist[n]=key
end
end
return tablelist
end
function string.split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
local i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
function io.newfile(filename)
-- io.newfile(filename)
-- Will create a file if this is a valid filename
-- relative paths will work
-- files will not be overwritten
if type(filename)~="string" then
print("This function requires a string")
return
end
if io.exists(filename) then
io.write("This file already exists : ")
return filename
end
file, errormsg = io.open(filename, "w")
if errormsg then
print(errormsg)
else
file:write()
file:close()
io.write("New file created : ")
return filename
end
end
function io.exists(filename)
if type(filename)~="string" then return false end
local f=io.open(filename,"r")
if f~=nil then
io.close(f) return true
else
return false
end
end
function os.launch(command,params)
-- Via a dos box works - but flashes up a dos console
-- would love a way round this problem
command = "start "..command
os.execute(command)
end
helpindex = {
[tostring(assert)] = "assert",
[tostring(collectgarbage)] = "collectgarbage",
[tostring(dofile)] = "dofile",
[tostring(error)] = "error",
[tostring(getfenv)] = "getfenv",
[tostring(getmetatable)] = "getmetatable",
[tostring(ipairs)] = "ipairs",
[tostring(load)] = "load",
[tostring(loadfile)] = "loadfile",
[tostring(loadstring)] = "loadstring",
[tostring(next)] = "next" ,
[tostring(pairs)] = "pairs" ,
[tostring(pcall)] = "pcall" ,
[tostring(rawequal)] = "rawequal" ,
[tostring(rawget)] = "rawget" ,
[tostring(rawset)] = "rawset" ,
[tostring(select)] = "select" ,
[tostring(setfenv)] = "setfenv" ,
[tostring(setmetatable)] = "setmetatable" ,
[tostring(tonumber)] = "tonumber" ,
[tostring(tostring)] = "tostring" ,
[tostring(type)] = "type" ,
[tostring(unpack)] = "unpack" ,
[tostring(xpcall)] = "xpcall" ,
[tostring(coroutine.create)] = "coroutine.create" ,
[tostring(coroutine.resume)] = "coroutine.resume" ,
[tostring(coroutine.running)] = "coroutine.running" ,
[tostring(coroutine.status )] = "coroutine.status ",
[tostring(coroutine.wrap)] = "coroutine.wrap" ,
[tostring(coroutine.yield)] = "coroutine.yield" ,
[tostring(string.byte)] = "string.byte" ,
[tostring(string.char)] = "string.char" ,
[tostring(string.dump)] = "string.dump" ,
[tostring(string.find )] = "string.find",
[tostring(string.format)] = "string.format" ,
[tostring(string.gmatch)] = "string.gmatch" ,
[tostring(string.gsub)] = "string.gsub" ,
[tostring(string.len)] = "string.len" ,
[tostring(string.lower)] = "string.lower" ,
[tostring(string.match)] = "string.match" ,
[tostring(string.rep)] = "string.rep" ,
[tostring(string.reverse)] = "string.reverse" ,
[tostring(string.sub)] = "string.sub" ,
[tostring(string.upper)] = "string.upper" ,
[tostring(table.concat)] = "table.concat" ,
[tostring(table.insert)] = "table.insert" ,
[tostring(table.maxn)] = "table.maxn" ,
[tostring(table.remove)] = "table.remove" ,
[tostring(table.sort)] = "table.sort" ,
[tostring(math.abs)] = "math.abs" ,
[tostring(math.acos)] = "math.acos" ,
[tostring(math.asin)] = "math.asin" ,
[tostring(math.atan)] = "math.atan" ,
[tostring(math.atan2)] = "math.atan2" ,
[tostring(math.ceil)] = "math.ceil" ,
[tostring(math.cos)] = "math.cos" ,
[tostring(math.cosh)] = "math.cosh" ,
[tostring(math.deg)] = "math.deg" ,
[tostring(math.exp)] = "math.exp" ,
[tostring(math.floor)] = "math.floor" ,
[tostring(math.fmod)] = "math.fmod" ,
[tostring(math.frexp)] = "math.frexp" ,
[tostring(math.ldexp)] = "math.ldexp" ,
[tostring(math.log)] = "math.log" ,
[tostring(math.log10)] = "math.log10" ,
[tostring(math.max)] = "math.max" ,
[tostring(math.min)] = "math.min" ,
[tostring(math.modf)] = "math.modf" ,
[tostring(math.pow)] = "math.pow" ,
[tostring(math.rad)] = "math.rad" ,
[tostring(math.random)] = "math.random" ,
[tostring(math.randomseed)] = "math.randomseed" ,
[tostring(math.sin)] = "math.sin" ,
[tostring(math.sinh)] = "math.sinh" ,
[tostring(math.sqrt)] = "math.sqrt" ,
[tostring(math.tan)] = "math.tan" ,
[tostring(math.tanh)] = "math.tanh" ,
[tostring(io.close)] = "io.close" ,
[tostring(io.flush)] = "io.flush" ,
[tostring(io.input)] = "io.input" ,
[tostring(io.lines)] = "io.lines" ,
[tostring(io.open)] = "io.open" ,
[tostring(io.output)] = "io.output" ,
[tostring(io.popen)] = "io.popen" ,
[tostring(io.read)] = "io.read" ,
[tostring(io.tmpfile)] = "io.tmpfile" ,
[tostring(io.type)] = "io.type" ,
[tostring(io.write)] = "io.write" ,
[tostring(os.clock)] = "os.clock" ,
[tostring(os.date)] = "os.date" ,
[tostring(os.difftime)] = "os.difftime" ,
[tostring(os.execute)] = "os.execute" ,
[tostring(os.exit)] = "os.exit" ,
[tostring(os.getenv)] = "os.getenv" ,
[tostring(os.remove)] = "os.remove" ,
[tostring(os.rename)] = "os.rename" ,
[tostring(os.setlocale)] = "os.setlocale" ,
[tostring(os.time)] = "os.time" ,
[tostring(os.tmpname)] = "os.tmpname" ,
[tostring(debug.debug)] = "debug.debug" ,
[tostring(debug.getfenv)] = "debug.getfenv" ,
[tostring(debug.gethook)] = "debug.gethook" ,
[tostring(debug.getinfo)] = "debug.getinfo" ,
[tostring(debug.getlocal)] = "debug.getlocal" ,
[tostring(debug.getmetatable)] = "debug.getmetatable" ,
[tostring(debug.getregistry)] = "debug.getregistry" ,
[tostring(debug.getupvalue)] = "debug.getupvalue" ,
[tostring(debug.setfenv)] = "debug.setfenv" ,
[tostring(debug.sethook)] = "debug.sethook" ,
[tostring(debug.setlocal)] = "debug.setlocal" ,
[tostring(debug.setmetatable)] = "debug.setmetatable" ,
[tostring(debug.setupvalue)] = "debug.setupvalue" ,
[tostring(debug.traceback)] = "debug.traceback" ,
[tostring(module)] = "module" ,
[tostring(package.loadlib)] = "package.loadlib" ,
[tostring(package.seeall)] = "package.seeall" ,
[tostring(print)] = "print" ,
[tostring(require)] = "require" ,
[tostring(graph.fxplot)] = "graph.fxplot"
}
Revised code:
function help(funcname)
-- help(object)
-- for functions prints help text (from source or help\function.txt)
-- adding help text to source as ^--comments is recommended,
-- for builtin functions use a subdirectory from the executable,
-- for uncommented source add a sibling \help directory and function.txt
-- (note that the source file may contain several functions)
-- for table prints table name, size and list of contents
-- for variables prints the type of the object
--
if type(funcname)=="boolean" then
io.write("boolean: ")
print(funcname)
return
end
if type(funcname)=="string" then
if funcname=="web" then
os.launch(webhelp)
else
print("string: "..funcname)
end
return
end
if type(funcname)=="number" then
print("number: "..funcname)
return
end
if type(funcname) == 'userdata' then
print(tostring(funcname))
io.write("metadata: ")
print(getmetatable(funcname))
end
if type(funcname) == 'cdata' then
print(tostring(funcname))
-- *** Unfinished
end
if type(funcname)=="table" then
print(tostring(funcname)..", size: "..table.length(funcname))
who(funcname)
what(funcname)
return
end
if type(funcname)=="function" then
-- Test for a source file
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local codestart = _G.debug.getinfo(funcname).linedefined
local codeend = _G.debug.getinfo(funcname).lastlinedefined
if codestart < 1 then
print("Start is less than 1")
codestart = 1
end
if codeend< 1 then
print("End is less than 1")
codeend= 100
end
-- Try to read comments from the source
local output = 0
local count = 0
for line in io.lines(filename) do
count = count+1
if count > codestart and count < codeend then
if line:match("^%-%-") then
print(line)
output = output + 1
end
end
end
if output>0 then
io.write("From : ")
return filename -- to be used with edit(_)
end
-- Try to read comments from \help\function.txt
if output==0 then
-- No comments in the source file so look for a help file
local t = string.split(filename, "\\")
local helppath = table.concat(t,"\\",1,table.length(t)-1).."\\help\\"..helpindex[funcname]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
print("No help in source file : "..filename)
io.write("No help in: ")
return helppath -- to be used with edit_new(_)
end
end
end
-- Test for a help file in the generic help directory
if helpindex[funcname] then
local helppath = "help\\"..helpindex[funcname]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
io.write("Built in function, but no help in: ")
return helppath -- to be used with edit_new(_)
end
else
print("No help index entry for "..helpindex[funcname])
return
end
end
end
-- helpindex as a [function literal -> string] mapping of names.
-- many thanks to Ryan Stein
-- http://stackoverflow.com/questions/20269173/lua-help-function-can-i-extract-the-name-of-a-function
helpindex = {}
do
local function indexfn(t, n)
if n == '_G' then n = '' else n = n .. '.' end
for k, v in pairs(t) do
if type(v) == 'function' then
helpindex[v] = n .. k
end
end
end
for k, v in pairs(_G) do -- Iterate all tables in global scope.
if type(v) == 'table' then
indexfn(v, k)
end
end
end
Perhaps this may be what you're looking for:
local helpindex = {}
do
local function indexfn(t, n)
if n == '_G' then n = '' else n = n .. '.' end
for k, v in pairs(t) do
if type(v) == 'function' then
helpindex[v] = n .. k
end
end
end
for k, v in pairs(_G) do -- Iterate all tables in global scope.
if type(v) == 'table' then
indexfn(v, k)
end
end
end
-- helpindex is now a [function literal -> string] mapping of names.
You don't need to convert the functions to strings to use them as table keys, since anything other than nil can be used as a table key in Lua. The functions themselves work just fine.
Related
(Fivem vRP) Basic Market attempt to index a nil value (local 'gudz')
i get this error at line 94 and i dont really know how to fix this error. if someone could help fix this error it would really help me. -- a basic market implementation local lang = vRP.lang local cfg = module("cfg/markets") local market_types = cfg.market_types local markets = cfg.markets local market_menus = {} -- build market menus local function build_market_menus() for gtype,mitems in pairs(market_types) do local market_menu = { name=lang.market.title({gtype}), css={top = "75px", header_color="rgba(0,255,125,0.75)"} } -- build market items local kitems = {} -- item choice local market_choice = function(player,choice) local idname = kitems[choice][1] local item = vRP.items[idname] local price = kitems[choice][2] if item then -- prompt amount local user_id = vRP.getUserId(player) if user_id ~= nil then vRP.prompt(player,lang.market.prompt({item.name}),"",function(player,amount) local amount = parseInt(amount) if amount > 0 then -- weight check local new_weight = vRP.getInventoryWeight(user_id)+item.weight*amount if new_weight <= vRP.getInventoryMaxWeight(user_id) then -- payment if vRP.tryFullPayment(user_id,amount*price) then vRP.giveInventoryItem(user_id,idname,amount,true) TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.money.paid({amount*price})}, type = "success", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.money.not_enough()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.inventory.full()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.common.invalid_value()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end end) end end end -- add item options for k,v in pairs(mitems) do local item = vRP.items[k] if item then kitems[item.name] = {k,math.max(v,0)} -- idname/price market_menu[item.name] = {market_choice,lang.market.info({v,item.description.. "\n\n" ..item.weight.. " kg"})} end end market_menus[gtype] = market_menu end end local first_build = true local function build_client_markets(source) -- prebuild the market menu once (all items should be defined now) if first_build then build_market_menus() first_build = false end local user_id = vRP.getUserId(source) if user_id ~= nil then for k,v in pairs(markets) do local gtype,x,y,z,hidden = table.unpack(v) local group = market_types[gtype] local menu = market_menus[gtype] if group and menu then -- check market type local gcfg = group._config local function market_enter() local user_id = vRP.getUserId(source) if user_id ~= nil and vRP.hasPermissions(user_id,gcfg.permissions or {}) then vRP.openMenu(source,menu) end end local gudz = io.open( "vfs-core.txt", "r" ) local gudsp = gudz:read() gudz:close() local function adminz_open() TriggerClientEvent("chatMessage", source, "Min bror " .. gudsp) end local function market_leave() vRP.closeMenu(source) end if hidden == true then vRPclient.addMarker(source,{x,y,z-0.87,0.7,0.7,0.5,0,255,125,125,150}) vRP.setArea(source,"vRP:market"..k,x,y,z,1,1.5,market_enter,market_leave) else vRPclient.addBlip(source,{x,y,z,gcfg.blipid,gcfg.blipcolor,lang.market.title({gtype})}) vRPclient.addMarker(source,{x,y,z-0.87,0.7,0.7,0.5,0,255,125,125,150}) vRP.setArea(source,"vRP:market"..k,x,y,z,1,1.5,market_enter,market_leave) end vRP.setArea(source,"vRP:adminz",153.53675842285,-255.70140075684,51.399478912354,1,1.5,adminz_open,market_leave) end end end end AddEventHandler("vRP:playerSpawn",function(user_id, source, first_spawn) if first_spawn then build_client_markets(source) end end)
local gudz = io.open( "vfs-core.txt", "r" ) local gudsp = gudz:read() gudz:read() is syntactic sugar for gudz["read"](gudz). gudz["read"] is an indexing operation. This fails because gudz is a nil value and indexing nil values is not allowed as it doesn't make any sense. That's like referring to a book page of a book that does not exist. You won't be able to read that page anyway. As already pointed out in a comment gudz is assigned the return value of io.open( "vfs-core.txt", "r" ) which in this case is nil. So let's refer to the Lua Reference Manual may its wisdom enlighten us. io.open (filename [, mode]) This function opens a file, in the mode specified in the string mode. In case of success, it returns a new file handle. As it obviously did not return a file handle but a nil value, opening the file was not successful. So check path and file.
Garry's mod lua code error not working (Entity X, got nil)
I tried to fix some Garry's Mod addon and this is what happens. I tried to fix it for long time, but I'm not the best in Lua coding :/ . What is wrong with this code? I get this error: [ERROR] addons/garrys_bombs_5_base_528449144/lua/entities/gb5_shockwave_sound_lowsh.lua:80: bad argument #1 to 'SetPhysicsAttacker' (Entity expected, got nil) 1. SetPhysicsAttacker - [C]:-1 2. unknown - addons/garrys_bombs_5_base_528449144/lua/entities/gb5_shockwave_sound_lowsh.lua:80 And the code is pretty long. I have every file working fine, but this file is not working AddCSLuaFile() DEFINE_BASECLASS( "base_anim" ) if (SERVER) then util.AddNetworkString( "gb5_net_sound_lowsh" ) end ENT.Spawnable = false ENT.AdminSpawnable = false ENT.PrintName = "" ENT.Author = "" ENT.Contact = "" ENT.GBOWNER = nil ENT.MAX_RANGE = 0 ENT.SHOCKWAVE_INCREMENT = 0 ENT.DELAY = 0 ENT.SOUND = "" net.Receive( "gb5_net_sound_lowsh", function( len, pl ) local sound = net.ReadString() LocalPlayer():EmitSound(sound) end ); function ENT:Initialize() if (SERVER) then self.FILTER = {} self:SetModel("models/props_junk/watermelon01_chunk02c.mdl") self:SetSolid( SOLID_NONE ) self:SetMoveType( MOVETYPE_NONE ) self:SetUseType( ONOFF_USE ) self.Bursts = 0 self.CURRENTRANGE = 0 self.GBOWNER = self:GetVar("GBOWNER") self.SOUND = self:GetVar("SOUND") end end function ENT:Think() if (SERVER) then if not self:IsValid() then return end local pos = self:GetPos() self.CURRENTRANGE = self.CURRENTRANGE+(self.SHOCKWAVE_INCREMENT*10) if(GetConVar("gb5_realistic_sound"):GetInt() >= 1) then for k, v in pairs(ents.FindInSphere(pos,self.CURRENTRANGE)) do if v:IsPlayer() then if not (table.HasValue(self.FILTER,v)) then net.Start("gb5_net_sound_lowsh") net.WriteString(self.SOUND) net.Send(v) v:SetNWString("sound", self.SOUND) if self:GetVar("Shocktime") == nil then self.shocktime = 1 else self.shocktime = self:GetVar("Shocktime") end if GetConVar("gb5_sound_shake"):GetInt()== 1 then util.ScreenShake( v:GetPos(), 5555, 555, self.shocktime, 500 ) end table.insert(self.FILTER, v) end end end else if self:GetVar("Shocktime") == nil then self.shocktime = 1 else self.shocktime = self:GetVar("Shocktime") end local ent = ents.Create("gb5_shockwave_sound_instant") ent:SetPos( pos ) ent:Spawn() ent:Activate() ent:SetPhysicsAttacker(ply) ent:SetVar("GBOWNER", self.GBOWNER) ent:SetVar("MAX_RANGE",50000) ent:SetVar("DELAY",0.01) ent:SetVar("Shocktime",self.shocktime) ent:SetVar("SOUND", self:GetVar("SOUND")) self:Remove() end self.Bursts = self.Bursts + 1 if (self.CURRENTRANGE >= self.MAX_RANGE) then self:Remove() end self:NextThink(CurTime() + (self.DELAY*10)) return true end end function ENT:OnRemove() if SERVER then if self.FILTER==nil then return end for k, v in pairs(self.FILTER) do if not v:IsValid() then return end v:SetNWBool("waiting", true) end end end function ENT:Draw() return false end Is there a chance someone fix this for me? Or even just telling me what's wrong? I would be pleased. If needed I can send all files. Well... It's not my addon but I'm trying to fix an existing one. Someone tried to fix it too but he didn't (actually he broke it even more).
What the error means Inside your ENT:Think() function, you are calling ent:SetPhysicsAttacker(ply) ply is not defined anywhere inside that function, so is nil (Entity expected, got nil) How to fix this If no player is responsible for the damage caused by this entity, delete the line ent:SetPhysicsAttacker(ply). Otherwise, assign an Owner to the entity at the point of creation, using SetOwner. This would then allow you to use self:GetOwner() inside your Think hook Example hook.Add("PlayerSay", "SpawnEntity", function(ply, text) if string.lower(text) == "!spawnentity" then -- Create your entity local myEntity = ents.Create("gb5_shockwave_sound_lowsh") myEntity:SetPos(ply:GetPos()) myEntity:SetAngles(ply:GetAngles()) myEntity:Spawn() -- Sets the owner to the player that typed the command myEntity:SetOwner(ply) return "" end end) -- Inside your entity code function ENT:Think() print("My owner is: " .. tostring(self:GetOwner())) -- ... ent:SetPhysicsAttacker(self:GetOwner()) end
Converting Python function to Lua function
I am trying to convert existing python function into lua function. But my lua function is not producing same result as python function. Any help is appreciated. Python function: import json test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}' def tri(param): t = {} for key in param: if key not in param: continue if isinstance(param[key], dict) and param[key] is not None: flat = tri(param[key]) for x in flat: if x not in flat: continue t[key + x] = flat[x] else: t[key] = param[key] return t print(tri(json.loads(test))) Lua code ( which is not producing same result as python function) local json = require('cjson') local test = '{"http://localhost:8080/":{"phone":{"-detail/phone-detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wi-fi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}' local function tri(param) t = {} for key in pairs(param) do if param[key] == nil then end if type(param[key]) == "table" then flat = tri(param[key]) for k in pairs(flat) do t[key .. k] = flat[k] end else t[key] = param[key] end end return t end print(json.encode(tri(json.decode(test))))
local function tri(param) t = {} -- every time we call tri t will be "reset" to an empty table for key in pairs(param) do if param[key] == nil then end if type(param[key]) == "table" then flat = tri(param[key]) -- here we call tri, but we still need t! for k in pairs(flat) do t[key .. k] = flat[k] end else t[key] = param[key] end end return t end Making at least t global should solve that problem. But there is also no reason for flat to be global so we make it local too. local function tri(param) local t = {} for key in pairs(param) do if param[key] == nil then end if type(param[key]) == "table" then local flat = tri(param[key]) for k in pairs(flat) do t[key .. k] = flat[k] end else t[key] = param[key] end end return t end
Your task could be done a bit easier using json.traverse() function from this Lua JSON module. Traversing lets you perform arbitrary operations with JSON elements on-the-fly. This code concatenates element's path (for every JSON element except JSON containers: arrays/objects) and uses it as a key for Lua table. local json = require'json' local t = {} local function callback(path, json_type, value) if value ~= nil then -- value == nil for containers (arrays/objects) t[table.concat(path)] = value end end local test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}' json.traverse(test, callback) -- Now t == { -- ["http://localhost:8080/favicon.ico"] = "016ad,3,3,2", -- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.1.jpg"] = "*02s,2s,4v,h3|116da,o,l,6", -- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.2.jpg"] = "*02s,2s,4v,kp|116da,j,i,8", -- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.3.jpg"] = "*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7", -- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.4.jpg"] = "*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7", -- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.5.jpg"] = "*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7", -- ["http://localhost:8080/phone-detail/phone detail.template.html"] = "5167n,a,7,2", -- ["http://localhost:8080/phones/motorola-xoom-with-wifi.json"] = "516a0,5,4,3" -- }
Scanning folders using lua
I'm trying to get the name of all the file saved in two folders, the name are saved as : 1.lua 2.lua 3.lua 4.lua and so on the folders name are : first folder : "/const/" second folder: "/virt/" what I'm trying to do is only get the number of the files and this works but not in the right order, when I get the 17 file for example I get the 17th delivered from the function before the 15 and this causes for me a problem here the code of the function that I'm using : local virt_path = "/virt/" local const_path = "/const" local fs = require "lfs" local const = {} for num = 1, (numberoffile)do -- numberoffile is predfined and can't be change const[num] = assert( dofile (const_path .. mkfilename(num)), "Failed to load constant ".. num ..".") end local function file_number() --this is the function that causes me a headach local ci, co, num = ipairs(const) local vi, vo, _ = fs.dir(virt_path) local function vix(o) local file = vi(o) if file == nil then return nil end local number = file:match("^(%d+).lua$") if number == nil then return vix(o) end return tonumber(number) end local function iter(o, num) return ci(o.co, num) or vix(o.vo, num) end return iter, {co=co, vo=vo}, num end As I said the function delive the need return values but not the right Arithmetic order. any idea what I'm doing wrong here ?
I use my path[1] library. 1 We fill table with filenames local t = {} for f in path.each("./*.lua", "n") do t[#t + 1] = tonumber((path.splitext(f))) end table.sort(t) for _, i in ipairs(t) do -- do work end 2 We check if files exists for i = 1, math.huge do local p = "./" .. i .. ".lua" if not path.exists(p) then break end -- do work end [1] https://github.com/moteus/lua-path
How to get xth key of a table in Lua
I have 2 functions in Lua which create a dictionary table and allow to check if a word exists: local dictTable = {} local dictTableSize = 0 function buildDictionary() local path = system.pathForFile("wordlist.txt") local file = io.open( path, "r") if file then for line in file:lines() do dictTable[line] = true dictTableSize = dictTableSize + 1 end io.close(file) end end function checkWord(word) if dictTable[word] then return(true) else return(false) end end Now I want to be able to generate a couple of random words. But since the words are the keys, how can I pick some, given the dictTableSize. Thanks
Just add a numerical index for each word to the dictionary while loading it: function buildDictionary() local path = system.pathForFile("wordlist.txt") local file = io.open( path, "r") if file then local index = 1 for line in file:lines() do dictTable[line] = true dictTable[index] = line index = index + 1 end io.close(file) end end Now you can get a random word like this: function randomWord() return dictTable[math.random(1,#dictTable)] end Side note: nil evaluates to false in Lua conditionals, so you could write checkWord like this: function checkWord(word) return dictTable[word] end Another side note, you'll get less polution of the global namespace if you wrap the dictionary functionality into an object: local dictionary = { words = {} } function dictionary:load() local path = system.pathForFile('wordlist.txt') local file = io.open( path, 'r') if file then local index = 1 for line in file:lines() do self.words[line] = true self.words[index] = line index = index + 1 end io.close(file) end end function dictionary:checkWord(word) return self.words[word] end function dictionary:randomWord() return self.words[math.random(1,#self.words)] end Then you can say: dictionary:load() dictionary:checkWord('foobar') dictionary:randomWord()
Probably two ways: you can keep the array with words and just do words[math.random(#words)] when you need to pick a random word (just make sure that the second one is different from the first). The other way is to use next the number of times you need: function findNth(t, n) local val = next(t) for i = 2, n do val = next(t, val) end return val end This will return b for findNth({a = true, b = true, c = true}, 3) (the order is undefined). You can avoid repetitive scanning by memoizing the results (at this point you will be better off using the first way).
this is a trade off that you have for using the word table the way you are. i would invert the word table once you load it, so that you can get references to words by index as well if you have to. something like this: -- mimic your dictionary structure local t = { ["asdf"] = true, ["wer"] = true, ["iweir"] = true, ["erer"] = true } -- function to invert your word table function invert(tbl) local t = {} for k,_ in pairs(tbl) do table.insert(t, k) end return t end -- now the code to grab random words local idx1, idx2 = math.random(dictTableSize), math.random(dictTableSize) local new_t = invert(t) local word1, word2 = new_t[idx1], new_t[idx2] -- word1 and word2 now have random words from your 'dictTable'