I'm outputting functions to strings so I can concatenate them. However, stuff like this happens. Tech output: Code: [Info] Current tech aim position: table: 0x7f2fa0be7540 [Error] Exception while calling script init: (LuaException) Error code 2, [string "/tech/distortionsphere/techdebug.lua"]:12: attempt to index a nil value (global 'player') stack traceback: [C]: in metamethod '__index' [string "/tech/distortionsphere/techdebug.lua"]:12: in global 'debugCommands' [string "/tech/distortionsphere/techdebug.lua"]:6: in function <[string "/tech/distortionsphere/techdebug.lua"]:5> Tech Lua: Code: require "/scripts/vec2.lua" require "/scripts/keybinds.lua" --create binding on f to run the thing function init() Bind.create("f", debugCommands()) end --the meat of the thing function debugCommands() sb.logInfo("Current tech aim position: " .. tostring(tech.aimPosition())) sb.logInfo("Current gender/species: " .. tostring(player.gender()) .. " / " .. tostring(player.species())) sb.logInfo("Current player entity ID: " .. tostring(player.id())) sb.logInfo("Current unique player ID: " .. tostring(player.uniqueID())) if player.isAdmin() == true then sb.logInfo("Player is currently the Admin.") else sb.logInfo("Player is not currently the Admin.") end end function update(args) end function uninit() end I attempted to use typeof() on player, but that didn't help as it returned a similar result (attempt to index a nil value). I have little to no clue what's going on anymore.
This may help: http://community.playstarbound.com/threads/auto-lounge-the-player-in-a-vehicle.135855/
Didn't help. I attempted to call the functions as a variable in init(), but the same error happened with init(). However, debugCommands() didn't fail this time, but outputted "nil" for all the player.*() functions. Output: Code: [Info] Current tech aim position: table: 0x7f59b6d3c180 [Info] Current gender/species: nil / nil [Info] Current player entity ID: nil [Info] Current unique player ID: nil [Error] Exception while calling script init: (LuaException) Error code 2, [string "/tech/distortionsphere/techdebug.lua"]:19: attempt to index a nil value (global 'player') stack traceback: [C]: in metamethod '__index' [string "/tech/distortionsphere/techdebug.lua"]:19: in global 'debugCommands' [string "/tech/distortionsphere/techdebug.lua"]:6: in function <[string "/tech/distortionsphere/techdebug.lua"]:5> New code: Code: require "/scripts/vec2.lua" require "/scripts/keybinds.lua" --create binding on f to run the thing function init() Bind.create("f", debugCommands()) gender = player.gender() species = player.species() pid = player.id() uid = player.uniqueId() end --the meat of the thing function debugCommands() sb.logInfo("Current tech aim position: " .. tostring(tech.aimPosition())) sb.logInfo("Current gender/species: " .. tostring(gender) .. " / " .. tostring(species)) sb.logInfo("Current player entity ID: " .. tostring(pid)) sb.logInfo("Current unique player ID: " .. tostring(uid)) if player.isAdmin() == true then sb.logInfo("Player is currently the Admin.") else sb.logInfo("Player is not currently the Admin.") end end function update(args) end function uninit() end EDIT: Maybe I read the post you linked wrong. I don't know. (Also I should make it pull values from the table instead of just turning it into a string, but whatever. That's a problem for another time.)
That only means you don't have a populated player table in the context you are calling this script. Did you try calling it from update() instead of init()? About the post I quoted, did you read all the thread? It's about possible contexts for accessing `player`
Tried that; here's the code: Code: require "/scripts/vec2.lua" require "/scripts/keybinds.lua" function init() end --the meat of the thing function debugCommands() sb.logInfo("Current tech aim position: " .. tostring(tech.aimPosition())) sb.logInfo("Current gender/species: " .. tostring(gender) .. " / " .. tostring(species)) sb.logInfo("Current player entity ID: " .. tostring(pid)) sb.logInfo("Current unique player ID: " .. tostring(uid)) if adminCheck then sb.logInfo("Player is currently the Admin.") else sb.logInfo("Player is not currently the Admin.") end end function update(args) gender = player.gender() species = player.species() pid = player.id() uid = player.uniqueId() adminCheck = player.isAdmin() if args.moves["special1"] then debugCommands() end end function uninit() end Now the code whines about the exact same thing but within update. As for that, I thought I read that correctly. Only the first half would be applicable here. Also, may I note that this is my first time using Lua? Maybe I should just read up on Lua a tad more...
Never tried to mod techs, but you probably can send messages between techs and entities which have access to player
Which entities would have access to player (other than quests)? My basic understanding of how the quest thing might work (although as of right now I have little to no clue how to code quests) is this: 1. Quest calls player functions 2. Quest outputs player functions to message handler 3. Message handler outputs to tech 4. Tech outputs to log
Or: 2. If logging is all you want, you can log directly from the quest itself. Code: The player table contains functions with privileged access to the player which run in a few contexts on the client such as scripted interface panes, quests, and player companions.
But then the problem is that then my log would get flooded to no end. Anyway, thanks for all the help and sticking with me 'till the end, @cpeosphoros .
Code: local doOnce = true function update(dt) -- ... if doOnce then doOnce = false -- log end -- ... end
That's correct. You can't access the player table from techs. To access the info from there, you'd need to use the messaging system, and hook the player scripts to add a handler to receive the message. But there should be easier ways, given that the world table is accessible from techs and should be able to get that info. Code: local id = entity.id() local species = world.entitySpecies(id) local gender = world.entityGender(id) local UUID = world.entityUniqueId(id) and so forth edit : If you want something to dump tables to the log nicely Code: makeString = function(someTable, baseName, str)--debug purposes only str = str or "" local isEmpty = true if someTable == nil then str = str .. baseName .. " : nil\n" return str end local blank = function(v) for _, __ in pairs(v) do return false end return true end for k, v in pairs(someTable) do isEmpty = false if type(v) == "table" then if (#v == 2) and (type(v[1]) == "number") and (type(v[2]) == "number") then --coordinate table str = str .. baseName .. "." .. tostring(k) .. " : {" .. v[1] .. ", " .. v[2] .. "}\n" elseif blank(v) then str = str .. baseName .. "." .. tostring(k) .. " : { }\n" else str = str .. makeString(v , baseName .. "." .. tostring(k), "") --.. "\n" --recursive calls don't necessarily need the original string because the main function will still have it end elseif (type(v) == "string") then str = str .. baseName .. "." .. tostring(k) .. " : \"" .. v .. "\"\n" elseif not (type(v) == "function") then str = str .. baseName .. "." .. tostring(k) .. " : " .. tostring(v) .. "\n" end end if isEmpty then str = str .. baseName .. " : { }\n" end return str end The first argument is a table you pass to it. The second argument is a string that describes the table - usually you'd use the table's own name The 3rd argument is an optional string that would go at the beginning. It will call itself recursively to handle nested tables. Use it like this - Code: local dump = makeString(storage, "storage", "\n") dump = makeString(self, "self", dump) sb.logInfo(dump) or Code: local dump = makeString(storage, "storage", "\n") dump = dump .. makeString(self, "self") sb.logInfo(dump) etc. I'll probably improve on it later, but I think you'll find that nice enough for your debugging needs as is. I'm also writing something similar to turn most LUA tables(some can't be translated) into copy/paste ready JSON.
If you go recursively you must treat the special case of looping references. Simple case: a = {1, 2, 3} a.b = a This will throw a stack overflow exception with table recursion. You will ran into it both with table-to-string and with deep copy algorithms. Here is my take on these: Code: -- @LICENSE MIT -- @author: CPE -- convenience function local function add(results, value) table.insert(results, value) end local cache -- add a value to results, quoting value if it's a string local function addValue(results, vPrefix, value, trail) local vValue if (type(value)=="string") then vValue = value:quotes() elseif (type(value)=="table") then vValue = tostring(value) .. " *" else vValue = tostring(value) end add(results, vPrefix .. vValue .. trail) end local _keys = {} local revKeys = {} local traverse = {} local traversing = false local errorLevel = 1 -- add a key,value pair to results, recursively if value is a table local function addPair(results, key, value, indent) errorLevel = errorLevel + 1 if table.isEmpty(_keys) or revKeys[key] or traversing then local oldTraversing = traversing traversing = traverse[key] ~= nil local vPrefix = indent .. tostring(key) .." : " -- check for recursivity - cache[value] is to avoid recursion loops if (type(value)=="table") and not cache[value] then cache[value] = true addValue(results, vPrefix, value, " {" ) table.formatted( value, _keys, indent .. string.rep(" ", tostring(key):len() + 8 ), results ) add(results, indent .. string.rep(" ", tostring(key):len() + 5 ).."},") else addValue(results, vPrefix , value, ",") end traversing = oldTraversing end errorLevel = errorLevel - 1 end function table:reverse() errorLevel = errorLevel + 1 -- not a table? Why are we messing with it? local rev = {} for i,v in ipairs(self) do rev[v] = i end errorLevel = errorLevel - 1 return rev end function table:isEmpty() return next(self) == nil end -- -- handle the first level special case -- @return a list of strings, not ending in "\n" formatted for printing function table:formatted (keys, indent, results) errorLevel = errorLevel + 1 if not keys or keys == "" then keys = {} end if type(keys) ~= "table" then keys = {keys} end _keys = keys traverse = table.reverse(_keys.traverse or {}) revKeys = table.reverse(_keys) --defaults indent = indent or "" local oldIndent = indent local firstLevel --first level if not results then results = {} cache = {} firstLevel = true end if firstLevel then --blank line, then curly oopen add(results, "") addValue(results, indent, self, " {" ) indent = indent .." " end -- adds all key,value pairs into results for key,value in pairs(self) do addPair(results, key, value, indent) end -- close the first level curly, then blank line if firstLevel then add(results, oldIndent.."}") add(results, "") --TODO: include flag for this --add(results, "Tables traversed:") --table.formatted (cache, "", results) end errorLevel = errorLevel - 1 -- the recursive call should ignore this. If it doesn't, an ugly mess insues return results end -- pretty printable string from a table function table:tostring( keys, indent) errorLevel = errorLevel + 1 local result = table.concat(table.formatted(self, keys, indent), "\n") errorLevel = errorLevel - 1 return result end -- convenience function function table:print(keys, indent) errorLevel = errorLevel + 1 print( table.tostring(self, keys, indent ) ) errorLevel = errorLevel - 1 end return table Code: -- @LICENSE MIT -- @author: CPE local cCache local cPromises local cLevel = 0 function table:deepCopy() cLevel = cLevel + 1 if cLevel == 1 then cCache = {} cPromises = {} end local copy if type(self) == 'table' then print(cLevel ..": ".. tostring(self)) if(cCache[self]) then -- print("Returning cached", cCache[self], "for", self) copy = cCache[self] else cPromises[self] = {} copy = {} for k, v in next, self, nil do print(k, v) local key = cPromises[k] or table.deepCopy(k) local value = cPromises[v] or table.deepCopy(v) copy[key] = value end setmetatable(copy, table.deepCopy(getmetatable(self))) -- print("Caching", copy, "for", self) cCache[self] = copy cPromises[self] = copy end else -- number, string, boolean, etc copy = self end cLevel = cLevel - 1 return copy end function table:shallowCopy() local copy = {} for k, v in pairs(self) do copy[k] = v end return copy end return table I already have working code for that, based on this great library: http://regex.info/blog/lua/json Code: -- @LICENSE MIT -- @author: CPE -- Strip C style comments from a string return function (pInput, pOutput) local function strip(str) local _, _, pre, com, pos = string.find(str, "^(.*)/%*(.+)%*/(.*)$") pre = pre or "" com = com or "" pos = pos or "" if com ~= "" then str = strip(pre .. pos) end return str end local function eolCom(str, pattern, subs) while str:find(pattern) do str = string.gsub(str, pattern, subs) end return str end repeat local str if type(pInput) == "function" then str = pInput() else str = pInput end if str then str = strip(str) str = eolCom(str,"(\".-\",)(%s*%/[%/%*].-)\n", "%1\n") str = eolCom(str,"(\n%s*%/[%/%*].-)\n", "\n") str = eolCom(str,"^(%s*%/[%/%*].-)\n", "\n") str = eolCom(str,"(%],?)%s*%/[%/%*].-\n", "%1\n") end if type(pOutput) == "function" then pOutput(str) else return str end until not str end Code: -- @LICENSE MIT -- @author: CPE local stripComments = require ("stripComments") -- Decodes json files into tables. We use Jeffrey Friedl's JSON.lua for being -- pure Lua and providing error treatment callbacks, which we need for stray C -- style comments. return function (pInput, pOutput) local JSON = require "JSON" local decodeError = {} function JSON:onDecodeError(message, text, location) decodeError = {location, message, text} end local function tryFix(text, pos) local prefix = string.sub(text, 1, pos - 1) local suffix = string.sub(text, pos) local str = prefix..stripComments(suffix) if str ~= text then return JSON:decode(str) end end function JSON:onTrailingGarbage(json_text, location) local res = tryFix(json_text, location) if res then return res end return nil, "trailing garbage" end local str if type(pInput) == "function" then str = pInput() end if type(pInput) == "string" then str = pInput end if not str then return end local res = JSON:decode(str) local isError = #decodeError ~= 0 local fakeErrors ={ ["expected comma or '}'"] = true, ["expected colon" ] = true, } if isError and fakeErrors[decodeError[2]] then local pos = decodeError[1] local text = decodeError[3] local fix = tryFix(text, pos) if fix then res = fix isError = false end end local function returns() return isError, isError and decodeError or res end if type(pOutput) == "function" then pOutput(returns()) else return returns() end end Usage: Code: -- @LICENSE MIT -- @author: CPE local stripComments = require "stripComments" local jsonDecode = require "jsonDecode" local assetCache = {} function getJsonAsset(path) if not assetCache[path] then local src = readfile(path) src = stripComments(src) local isError, res = jsonDecode(src) if isError then error ("Invalid json file "..path.." "..res[2].." at pos "..res[1]..".") end assetCache[path] = res end return assetCache[path] end local table = getJsonAsset("/path/to/whatever.object") readfile() is a function which reads the entire contents of a file, preserving line breaks. As for transforming a table into json, JSON:encode(tbl) , from Jeffrey Friedl's JSON.lua, will handle almost any imaginable case.