This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
aadrik's profile picture

Calling private functions from another API

Started by aadrik, 09 February 2016 - 09:07 AM
aadrik #1
Posted 09 February 2016 - 10:07 AM
I have made an API for my turtles to learn objects and save the object name to a file so that the can continually build a database of known object for easy comparison to later and I want to use some of these functions in another API that I am building that manages fuel. However when I call a function from my LearnAPI from within my FuelAPI I get a error FuelAPI:line20 attempt to index ? (nil). I have posted both my APIs. I have been stuck on this for several hours and nothing I do is making any difference. Also I will post my startup script in case that helps.

startup

os.loadAPI("LocationAPI")
print("Loaded Location API")
os.loadAPI("LearnAPI")
print("Loaded LearnAPI")
os.loadAPI("FuelAPI")
print("Loaded FuelAPI")
location = LocationAPI.Setup()
learn = LearnAPI.setup()
fuel = FuelAPI.setup()
fuel.setFuelSource()

LearnAPI

--Allows Turtle to learn new objects--
function setup()
local this = {
  learned_items = {}
}
local saveItems = function ()
  save = fs.open("known_blocks", "w")
  save.write (textutils.serialize({this.learned_items}))
  save.close()
end
local loadItems = function ()
  if fs.exists("known_blocks") then
   load = fs.open("known_blocks", "r")
   this.learned_items = unpack(textutils.unserialize(load.readAll()))
   print("...Loading known items...")
   load.close()
  else
   print("I dont know any blocks.")
  end
end
local getLearnedItems = function ()
  loadItems()
  for i = 1, #this.learned_items do
   print(this.learned_items[i])
  end
end
local known = function (search)
  loadItems()
  for i = 1, #this.learned_items do
   if search == this.learned_items[i] then
	return true
   end
  end
end
local scan = function ()
  loadItems()
  local success, item = turtle.inspect()
   if success then
	if known(item.name) == true then
	 print("Item already known.")
	else
	 table.insert(this.learned_items, item.name)
	 print ("Learned ", item.name)
	 saveItems()
	end
   end
end

local scanInventory = function()
  loadItems()
  for i = 1, 16 do
   if turtle.getItemCount(i) ~= 0 then
	local item = turtle.getItemDetail(i)
	if known(item.name) == true then
	 print("Item already known.")
	else
	 table.insert(this.learned_items, item.name)
	 print ("Learned ", item.name)
	 saveItems()
	end
   end
  end
end

return {
  scan = scan,
  getLearnedItems = getLearnedItems,
  scanInventory = scanInventory,
  known = known
}
end


FuelAPI

function setup()
local this = {
  min_fuel_level = 10,
  fuel_sources = {"minecraft:coal", "minecraft:planks", "minecraft:log"},
}

local refuel = function ()
  if turtle.getFuelLevel() < this.min_fuel_level then
   turtle.refuel()
  end
end

local setMinFuelLevel = function (min_level)
  this.min_fuel_level = min_level
  print("Minimum fuel level set to " , this.min_fuel_level)
end

local setFuelSource = function ()
  print("in fuel")
  for i = 1, #this.fuel_sources do
   if learn.known(this.fuel_sources[i]) then
	print("I have ", this.fuel_sources[i])
   end
  end
end
return {
  refuel = refuel,
  setMinFuelLevel = setMinFuelLevel,
  setFuelSource = setFuelSource,
}
end
Bomb Bloke #2
Posted 09 February 2016 - 11:03 AM
Guess you mean line 21…

This is a matter of the environment. When a system starts its shell, it defines a new table (which is not _G!) and sets that as the environment for both the shell and any other scripts the user runs. Any global variables you define get dumped into there.

When you load an API, something similar happens: a new environment table is created, and the API's script is executed as a function that uses that table to handle all its globals. When it's done, os.loadAPI() takes all the values in the environment table, dumps them into a table named after the API, and dumps that into _G.

Meaning that any globals your regular scripts define, eg "learn", can't be seen by any APIs you load - they're not using the same environment table.

You'd hence be better off re-writing your scripts as regular APIs. Eg:

local this = {
  min_fuel_level = 10,
  fuel_sources = {"minecraft:coal", "minecraft:planks", "minecraft:log"},
}

refuel = function ()
  if turtle.getFuelLevel() < this.min_fuel_level then
   turtle.refuel()
  end
end

setMinFuelLevel = function (min_level)
  this.min_fuel_level = min_level
  print("Minimum fuel level set to " , this.min_fuel_level)
end

setFuelSource = function ()
  print("in fuel")
  for i = 1, #this.fuel_sources do
   if learn.known(this.fuel_sources[i]) then
        print("I have ", this.fuel_sources[i])
   end
  end
end

After os.loadAPI()'ing this from a file called "fuel", you'd then have access to eg fuel.setFuelSource() etc from both your regular scripts and from within any other APIs you load. Granted, APIs ideally shouldn't be tracking values like this within themselves, but I doubt you'll run into any issues doing things this way with your particular sort of functionality.

You could alternatively dump "learn" etc into _G, which your APIs do have access to, but that's a bit messy.
Edited on 09 February 2016 - 10:05 AM
aadrik #3
Posted 09 February 2016 - 11:22 AM
Thanks Bomb Bloke for the quick reply.
I did mean line 21, I had forgotten that I had placed another line in for debugging. Its funny that you said that I should just run them as regular APIs since that is what I just got finished testing right before checking the post and it worked, so thank you for your advice although I did it differently which leads me to my next question…
what is the difference between


setFuelSource = function ()
  print("in fuel")
  for i = 1, #this.fuel_sources do
   if learn.known(this.fuel_sources[i]) then
	    print("I have ", this.fuel_sources[i])
   end
  end
end

and


function setFuelSource()
  print("in fuel")
  LearnAPI.scan()
  for i = 1, #fuel_sources do
   if LearnAPI.known(fuel_sources[i]) then
    print("I have ", fuel_sources[i])
   end
  end
end

They both work so I dont really know the difference or is it more just a personal preference kind of thing? Thanks again for all your help.
Bomb Bloke #4
Posted 09 February 2016 - 11:56 AM
Well, one calls LearnAPI.scan(), and the other doesn't. ;)/>

In terms of the function definition thing, there is a minor difference, but it's irrelevant in most cases (and certainly in this one, as you're defining a global function). There's an explanation here under "local function syntax sugar" as to when and why it'd matter.
Edited on 09 February 2016 - 10:57 AM