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

Problem with a error, A question infact.[Solved]

Started by libraryaddict, 17 May 2012 - 05:31 AM
libraryaddict #1
Posted 17 May 2012 - 07:31 AM
Im getting the error "Index expected. Got nil" yet I know that the table was defined.
However I defined it locally in the API.

My question is. Can the local table in the API expire or something?

Somehow between this code
function playerJoin(ID, Name)
	ClientsNo[ID] = #Clients+1
	Clients[ClientsNo[ID]] = {}
	Clients[ClientsNo[ID]]["Name"] = Name
	Clients[ClientsNo[ID]]["ID"] = ID
	setPlayerMap(ID, "OrigSpawn")
end

and this code
function screenOptions(Locked, Width, Height, ID)
  Clients[ClientsNo[ID]]["LockedScreen"] = Locked
  Clients[ClientsNo[ID]]["ScreenWidth"] = Width
  Clients[ClientsNo[ID]]["ScreenHeight"] = Height
end

ClientsNo is now empty..

Im calling on them with a API with every table/variable local to the API.
Luanub #2
Posted 17 May 2012 - 07:40 AM
Is the var in the API and you are trying to use it from the program? If so you can not access it from the program.

I do use local vars in my API's and as long as I only use them in the API and not externally from a program I've never had any issues with them staying present as long as the API is loaded.
libraryaddict #3
Posted 17 May 2012 - 07:52 AM
All variables are set in my API.
The program itself just calls on the API.
Xtansia #4
Posted 17 May 2012 - 09:41 AM
Without seeing all your api code and such I can't say.
So if you can provide that it will be easier to help.
Luanub #5
Posted 17 May 2012 - 09:44 AM
So it should work, do you have something else in your code that is over writting/resetting the var?

I tried this little simple test and the var was stored and printed out correctly in the 2nd function. Load the first as an API and run the second as a program.
SpoilerAPI

local testVar = ""
local var = ""

function setVar( var )
testVar = var
end

function printVar()
if testVar ~= nil then
	print(testVar)
else print ("testVar = nil") end
end

Program

os.loadAPI("apitest")

apitest.setVar("testing")
apitest.printVar()
sleep(2)

os.unloadAPI("apitest")
Xtansia #6
Posted 17 May 2012 - 09:49 AM
So it should work, do you have something else in your code that is over writting/resetting the var?

I tried this little simple test and the var was stored and printed out correctly in the 2nd function. Load the first as an API and run the second as a program.
SpoilerAPI

local testVar = ""
local var = ""

function setVar( var )
testVar = var
end

function printVar()
if testVar ~= nil then
	print(testVar)
else print ("testVar = nil") end
end

Program

os.loadAPI("apitest")

apitest.setVar("testing")
apitest.printVar()
sleep(2)

os.unloadAPI("apitest")

There is no reason/need for you to declare
local var = ""
libraryaddict #7
Posted 17 May 2012 - 10:20 AM
My code then D:
Spoiler
local Clients = {} -- Stores all info on each client. Clients X/Y is based on their screen cords. use x y for client itself.
local ClientsNo = {}
local Servers = {} -- ID's of all the servers which rebroadcast the game for more range
local Maps = {}  -- Stores maps in it in another table. Can host unlimited maps.
local MapInfo = {} -- Stuff that was in the "Info:" This is so if there was a spawn then they spawn there. Or if you need to check so and so.
local Data = {} -- Stores data on each map, Is there a enemy here? A pickup pack?
local ViewMap = {} -- Every players viewable map is in here stored in its own seperate table
local Spawn = {} -- Define the spawn for each map
local DefaultMap = "Default" -- Map everyone first spawns on
local ChangedLines = {} -- Stores lines changed so it can try update the map with min lag
local ClientData = {"ID", "Name", "Map", "X", "Y", "Height", "Width"} -- Just add a bunch of names for each thingy so it knows it exists and can save it
local Explosions = {}
local MapSize = {}

function setDefault(map)
  DefaultMap = map
end

function sendClients(Info) -- Send the clients the info. This sends them all it :
  for n=1,#Clients do
	rednet.send(Clients[n], Info)
  end
end

function sendServers(Info) -- Send the servers the info. Sends them all of it.
  for n=1,#Servers do
	rednet.send(Servers[n], Info)
  end
end

function tag(sTag) -- Tags the messages so tagView can use it
  return "["..string.gsub(string.gsub(sTag, "%[", "~RightBracket~"), "%]", "~LeftBracket~").."]"
end

function tagView(msg) -- libraryaddict's little thingy ;D
  local ReturnMsg = msg
  local ReturnTag = {}
  if string.find(ReturnMsg, "%[") and string.find(ReturnMsg, "%]") then
	while true do
	  first = string.find(ReturnMsg, "%[", 1)
	  last = string.find(ReturnMsg, "%]", 2)
	  if first and last then ReturnTag[#ReturnTag+1] = string.gsub(string.gsub(string.sub(ReturnMsg, first+1, last-1), "~RightBracket~", "%["), "~LeftBracket", "%]")
		ReturnMsg = string.sub(ReturnMsg, last+1, string.len(msg))
	  else
		break
	  end
	end
	ReturnTag[#ReturnTag+1] = ReturnMsg
	return unpack(ReturnTag)
  else
	return false
  end
end

function loadMap(MapName, TableName) -- Loads a map into a table, The info included is loaded into another table
  if not MapName then error("function loadMap, Please use loadMap(MapNameAndDir, TableNameYouWantToAccessItWith)") end
  if not fs.exists(MapName) then error("Please use a map that exists!") end
  Maps[TableName] = {}
  local MapLoadVari = io.open(MapName, "r")
  local i = 1
  local a = 0
  local b = 0
  local Returns = {}
  for line in MapLoadVari:lines() do
	if not string.sub(line, 1, 5) == "Info:" then
	  Map[TableName][i] = line
	  b = b+1
	  if string.len(line) > a then
		a = string.len(line)
	  end
	else
	  Returns[#Returns+1] = string.sub(line, 6)
	end
	i = i+1
  end
  MapSize[TableName] = {}
  MapSize[TableName]["X"] = a
  MapSize[TableName]["Y"] = b
  loadObjects(TableName)
  Returns[#Returns+1] = TableName
  return unpack(Returns)
end
	
function setData(xInfo, yInfo, Map, InfoTable, InfoName, Info) -- Used for map data. Bahahaha confuse my stealers!
  local xInfo = tonumber(xInfo)
  local yInfo = tonumber(yInfo)
  if not Data[Map][yInfo] then
	Data[Map][yInfo] = {}
  end
  if not Data[Map][yInfo][xInfo] then
	Data[Map][yInfo][xInfo] = {}
  end
  if not Data[Map][yInfo][xInfo][InfoTable] then
	Data[Map][yInfo][xInfo][InfoTable] = {}
  end
  Data[Map][yInfo][xInfo][InfoTable][InfoName] = Info
end

function checkData(xInfo, yInfo, Map, InfoTable, InfoName) -- Return data at these cords.
  local xInfo = tonumber(xInfo)
  local yInfo = tonumber(yInfo)
  if InfoName then
	if Data[Map][yInfo][xInfo][InfoTable][InfoName] then
	  return unpack(Data[Map][yInfo][xInfo][InfoTable][InfoName])
	end
  else
	if Data[Map][yInfo][xInfo][InfoTable] then
	  return unpack(Data[Map][yInfo][xInfo][InfoTable])
	end
  end
end
	
function deleteData(xInfo, yInfo, Map, InfoTable)
  if Data[Map][yInfo][xInfo][InfoTable] then
	Data[Map][yInfo][xInfo][InfoTable] = nil
  end
end


function screenOptions(Locked, Width, Height, ID) -- No idea what screenlocked is gonna be good for, Locked must be true or false.
  Clients[ClientsNo[ID]]["LockedScreen"] = Locked
  Clients[ClientsNo[ID]]["ScreenWidth"] = Width
  Clients[ClientsNo[ID]]["ScreenHeight"] = Height
end

function moveScreen(Side, UpDown, ID, Map)
  if Clients[ClientsNo[ID]]["Y"]+UpDown >= 1 then
	Clients[ClientsNo[ID]]["Y"] = Clients[ClientsNo[ID]]["Y"]+UpDown
  elseif Clients[ClientsNo[ID]]["Y"]+UpDown <= MapSize[Map]["Y"] then
	Clients[ClientsNo[ID]]["Y"] = Clients[ClientsNo[ID]]["Y"]+UpDown
  else -- Wrap screen to edges.
	Clients[ClientsNo[ID]]["Y"] = MapSize[Map]["Y"]
  end
  if Clients[ClientsNo[ID]]["X"]+Side >= 1 then
	Clients[ClientsNo[ID]]["X"] = Clients[ClientsNo[ID]]["X"]+Side
  elseif  Clients[ClientsNo[ID]]["X"]+Side <= MapSize[Map]["X"] then
	Clients[ClientsNo[ID]]["X"] = Clients[ClientsNo[ID]]["X"]+Side
  else -- wrap
	Clients[ClientsNo[ID]]["X"] = MapSize[Map]["X"]
  end
  draw("all", ID, Map)
end

function setSpawn(MapName, XCord, YCord)
  Spawn[MapName] = {}
  Spawn[MapName]["X"] = XCord
  Spawn[MapName]["Y"] = YCord
end

function playerDataLoad(Name, ID)
  if fs.exists("PlayerData/"..Name) then
	Clients[#Clients+1] = {}
	local PlayerLoad = io.open(Name, "r")
	Clients[#Clients] = textutils.unserialize(PlayerLoad.readAll())
	ClientsNo[ID] = #Clients
	return true
  end
  return false
end



function playerJoin(ID, Name) -- When they sucessfully join. Assign them info.
  if not playerDataLoad(Name, ID) then
	ClientsNo[ID] = #Clients+1
	Clients[ClientsNo[ID]] = {}
	print("h:"..ClientsNo[ID])
	Clients[ClientsNo[ID]]["Name"] = Name
	Clients[ClientsNo[ID]]["ID"] = ID
	setPlayerMap(ID, "OrigSpawn")
  end
end

function setPlayerMap(ID, Place, X, Y)
  if Place == "OrigSpawn" then
	Clients[ClientsNo[ID]]["Map"] = DefaultMap
	local x, y = getSpawn(DefaultMap)
	Clients[ClientsNo[ID]]["X"] = x
	Clients[ClientsNo[ID]]["Y"] = y
  else
	Clients[ClientsNo[ID]]["Map"] = Place
	Clients[ClientsNo[ID]]["X"] = X
	Clients[ClientsNo[ID]]["Y"] = Y
  end
end
	

function playerDataSave(Name, ID)
  if not fs.exists("PlayerData") then
	fs.makeDir("PlayerData")
  end
  local PlayerSaving = io.open("PlayerData/"..Name, "w")
  PlayerSaving:write(textutils.serialize(Clients[ClientsNo[ID]]))
  PlayerSaving:close()
end

function split(str, pat)
   local t = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = "(.-)" .. pat
   local last_end = 1
   local s, e, cap = str:find(fpat, 1)
   while s do
	  if s ~= 1 or cap ~= "" then
	 table.insert(t,cap)
	  end
	  last_end = e+1
	  s, e, cap = str:find(fpat, last_end)
   end
   if last_end <= #str then
	  cap = str:sub(last_end)
	  table.insert(t, cap)
   end
   return unpack(t)
end

function getSpawn(Map) -- Get the point newbies spawn at
  return Spawn[Map]["X"], Spawn[Map]["Y"]
end

function draw(Info, ClientNo, Map) -- redraws for the client. Does NOT send the info out! Does NOT edit the map! Well it prob have to send it..
  if not ClientNo then error("draw(Info, ClientNo, Map) ClientNo not found. ClientNo is needed") end
  if Info == "all" then -- Draw every single appropriate line for the client. They now see the proper screen! Basically forcing a complete redraw. Eg if the screen moved.
	ViewMap[ClientNo] = {} -- Reset client map data. They now see a blank screen
	for n=1,Clients[ClientNo]["ScreenHeight"] do -- Client number bla bla. This will do it for their screenheight amount
	  ViewMap[ClientNo][n] = string.sub(Map[Clients[ClientNo]["Y"]+n], Clients[ClientNo]["X"], Clients[ClientNo]["ScreenWidth"])
	end
  elseif Info == "lines" then -- redraws a single line on their client. This should limit lag/flickering.
	for n=1,#ChangedLines do -- Gonna check with all the changed lines. But only the correct ones will be updated.
	  local Stuff = {split(ChangedLines[n], ":")}
	  if Clients[ClientNo]["Map"] == Stuff[1] then -- If the changed line is on the same map as the client
		--They are the same. You can now check if this client needs to see this line drawn. Or if its outside their screen scope
		-- OR I could just redraw it anyways >.>
		-- Wait a sec. The ChangedLines should just give the Map:Line:X thats being redrawn.
		-- So I need to check if the screen is in the X and Y of the changed.
		-- Could define each line like this. MAP:Y:X:X:X:X:X:X
		-- So the map got edited on line x. Each of these X's got edited. No way could it all be edited?
		-- I think I'll go with that
		if tonumber(Stuff[2]) >= Clients[ClientsNo[ID]]["Y"] and tonumber(Stuff[2]) <= Clients[ClientsNo[ID]]["Height"] then
		  local p = 3
		  repeat
			if tonumber(Stuff[p]) >= Clients[ClientsNo[ID]]["X"] and tonumber(Stuff[p]) <= Clients[ClientsNo[ID]]["Width"] then
			  --Ok. So you can redraw that line..
			  ViewMap[ClientNo][n] = string.sub(Map[Clients[ClientNo]["Map"]], Clients[ClientNo]["X"], Clients[ClientNo]["ScreenWidth"])
			  p = p+1
			end
		  until not Stuff[p]
		end
	  end
	end
  end
end
	  
function loadObjects(sTableName) -- Define the objects in here to be loaded into the data. This is for stuff like healthpacks etc defined with the map itself.
  if not sTableName then error("sTableName dont exist") end
  Objects = {"S", "L", "P"} -- Sample! Replace with your own. Letters it finds in the map..
  ObjectsData = {"Sample", "Libraryaddict", "Player"}
  for n=1,#Maps[sTableName] do -- For lines in that table do
	for q=1,string.len(Map[sTableName][n]) do
	  local sLetter = string.sub(Map[sTableName][n], q, q)
	  for p=1,#Objects do
		if Objects[p] == sLetter then
		  setData(xInfo, yInfo, sTableName, ObjectsData[p], "Type", ObjectsData[p])
		end
	  end
	end
  end
end

function editMap(xCord, yCord, Maps, Stuff) -- Edit data shown to player. Aka the map view. thingy.
  if Map[Maps][yCord] then
	Map[Maps][yCord] = string.sub(Map[Maps][yCord], 1, xCord-1)..Stuff..string.sub(Map[Maps][yCord], xCord+1, string.len(Map[Maps][yCord]))
  else error("editMap(), You wanted a yCord thats outside of the maps range.") end
end
	
function Tick() -- This defines the actions of everything that isnt the player. Eg explosion decay.
  spreadExplosion()
  MoveFired()
  MoveNPC()
  TheTick = os.startTimer(0.1)
end

function createExplosion(xCord, yCord, MapName, Spread, SpreadRate, DecayRate, Characters, ExplosionName, ...) -- All of this is needed. The "..." is directions. Use left, right, up, down if you want it to act like a normal explosion. ExplosionName is the kind of explosion this is, Eg. a nuke.
  --DataName is what you will see when you check Data
  setData(xInfo, yInfo, MapName, "Explosion", "Type", ExplosionName)
  local eNum = #Explosions+1
  Explosions[eNum] = {}
  local dirs = {...}
  local function def(sName, sData)
	Explosions[eNum][sName] = sData
  end
  for n=1,#dirs do
	dirs[n] = string.lower(dirs[n])
	if n == 5 then
	  error("Why are there 5 sides? error at createExplosion()")
	end
  end
  for n=1,#dirs do
	if dirs[n] == "up" then
	  def("Up", true)
	elseif dirs[n] == "down" then
	  def("Down", true)
	elseif dirs[n] == "left" then
	  def("Left", true)
	elseif dirs[n] == "right" then
	  def("Right", true)
	end
  end
  def("Map", MapName)
  def("X", xCord)
  def("Y", yCord)
  def("Spread", Spread)
  def("Rate", SpreadRate)
  def("Decay", DecayRate)
  def("Char", Character)
  def("Name", ExplosionName)
  def("STimer", 0)
  def("DTimer", DecayRate)
  setData(Explosions[EN]["X"], Explosions[EN]["Y"], Explosions[EN]["Map"], "Explosion", "Type", "Explosion")
  doExplosion(eNum)
end

--Add dirs! This is check for when I get back

function spreadExplosion() -- Finds all explosions and calls them. Pretty basic function but eh. I should stick this in Tick()
  for n=1,#Explosions do
	doExplosion(n)
  end
end

function deleteAllData(xInfo, yInfo, Map) -- Deletes all data there. Good for rewriting it :
  Data[Map][yInfo][xInfo] = {}
end

function isSolid(xInfo, yInfo, Map)
  if checkData(xInfo, yInfo, Map, "Solid", "Solid") == "Solid" then
	return true
  else
	return false
  end
end

function setSolid(xInfo, yInfo, Map)
  setData(xInfo, yInfo, Map, "Solid", "Solid", "Solid")
end

function unsetSolid(xInfo, yInfo, Map)
  setData(xInfo, yInfo, Map, "Solid", "Solid", "Not Solid")
end

function returnSide(xCord, yCord, Side)
  local Side = string.lower(Side)
  if Side == "left" then
	return (xCord-1), yCord
  elseif Side == "right" then
	return (xCord+1), yCord
  elseif Side == "up" then
	return xCord, (yCord-1)
  elseif Side == "down" then
	return xCord, (yCord+1)
  end
end

function returnExplodeData(EN)
  local KO = {}
  if Explosions[EN] then
	for n,a in ipairs(Explosions[EN]) do
	  KO[n] = a
	end
  end
  return unpack(KO)
end
	
function returnMap(ID)
  return unpack(ViewMap[ID])
end
	
--[[function doExplosion(EN) -- EN = ExplosionsNumber. This function auctally does the explosion checking.
  Explosions[EN]["STimer"] = Explosions[EN]["STimer"]-1
  Explosions[EN]["DTimer"] = Explosions[EN]["DTimer"]-1
  if Explosions[EN]["DTimer"] == 0 then -- Delete this one
	deleteData(Explosions[EN]["X"], Explosions[EN]["Y"], Explosions[EN]["Map"], "Explosion")
	table.remove(Explosions, EN)
	return
  end -- checkData(xInfo, yInfo, Map, InfoTable, InfoName)
  if Explosions[EN]["STimer"] <= 0 then--Spread
	Explosions[EN]["STimer"] = Explosions[EN]["SpreadRate"]
	local Side = {Explosions[EN]["Up"], Explosions[EN]["Down"], Explosions[EN]["Left"], Explosions[EN]["Right"]}
	local Sides = {Explosions[EN]["Up"], Explosions[EN]["Down"], Explosions[EN]["Left"], Explosions[EN]["Right"]}
	for n=1,Side do
	  local PigSnout = math.random(1,#Side) -- function createExplosion(xCord, yCord, MapName, Spread, SpreadRate, DecayRate, Characters, ExplosionName, ...) -- All of this is needed. The "..." is directions. Use left, right, up, down if you want it to act like a normal explosion. ExplosionName is the kind of explosion this is, Eg. a nuke.
	  if isSolid(returnSide(xInfo, yInfo, Side[PigSnout]), Map) == false then
		doExplosion(returnSide(xInfo, yInfo, Side[PigSnout])
		table.remove(Side, PigSnout)
	  end
	end
  end
end
end]]


What I used to test it. Minus the API loading. map is the API.
map.setSpawn("mappy", 5, 5)
map.loadMap("mappy", "Darling")
map.setDefault("mappy")
map.playerJoin(666, "Andrew", "mappy")
map.screenOptions("no", 10, 10, 1)

while true do
  event,param1,param2 = os.pullEvent()
  if event == "key" then
	if param1 == 200 then -- up
	  map.moveScreen(1, -1, 1, "mappy")
	  local y = {returnMap(1)}
	  for n=1,#y do
		term.write(y[n])
	  end
	elseif param1 == 208 then -- down
	  map.moveScreen(1, -1, 1, "mappy")
	  local y = {returnMap(1)}
	  for n=1,#y do
		term.write(y[n])
	  end
	elseif param1 == 203 then -- left
	  map.moveScreen(1, -1, 1, "mappy")
	  local y = {returnMap(1)}
	  for n=1,#y do
		term.write(y[n])
	  end
	elseif param1 == 205 then -- right
	  map.moveScreen(1, -1, 1, "mappy")
	  local y = {returnMap(1)}
	  for n=1,#y do
		term.write(y[n])
	  end
	end
  end
end


Pastebin doesnt like me atm :
libraryaddict #8
Posted 17 May 2012 - 10:22 AM
I guess you would want a mappy too..

I just used a thingy to generate code in a 200x200 box.
mappy isn't important. Just needs a file.

Also the stuff after while true do isn't important either.

Also please consider my API is a WIP..
Heh. Just realised my second program had a derp.
But nothing that effected the API..

Also. ClientsNo is the one thats getting reset.
Cloudy #9
Posted 17 May 2012 - 12:47 PM
I guess you would want a mappy too..

I just used a thingy to generate code in a 200x200 box.
mappy isn't important. Just needs a file.

Also the stuff after while true do isn't important either.

Also please consider my API is a WIP..
Heh. Just realised my second program had a derp.
But nothing that effected the API..

Also. ClientsNo is the one thats getting reset.

And we fixed this in IRC it is worth pointing out - you were setting up one ID and then trying to index the table by another ID - meaning the index in the table was nil, NOT the table.
libraryaddict #10
Posted 17 May 2012 - 01:07 PM
Also I was making it more complex then I needed it to be.