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

Webbrowser, works first time, then fails second time.

Started by lifewcody, 05 November 2014 - 08:54 PM
lifewcody #1
Posted 05 November 2014 - 09:54 PM
Browser code:

-- Variables --
dir = shell.dir().."/"
settings = {}
ID = os.getComputerID()
os.loadAPI("/loc/browser/includes/inzernet")
inzernet.startup()
-- Top Bar --
function topBar()
  term.setCursorPos(6, 1)
  term.setBackgroundColor(colors.white)
  local URL = read()
  modemMes(URL)
end
-- Draw Main Function --
function drawMain()
  term.setBackgroundColor(colors.lightGray)
  term.clear()
  paintutils.drawLine(1, 1, 51, 1, colors.gray)
  term.setCursorPos(1, 1)
  write("URL: ")
  paintutils.drawLine(6, 1, 50, 1, colors.white)
  paintutils.drawPixel(51, 1, colors.red)
  term.setCursorPos(51, 1)
  write("X")
  term.setCursorPos(6, 1)
  term.setBackgroundColor(colors.white)
  webDis = window.create(term.current(), 1, 2, 51, 18)
  term.setTextColor(colors.gray)
  topBar()
end
-- Web Display --
function display(file, url)
  term.redirect(webDis)
  term.setTextColor(colors.white)
  term.setBackgroundColor(colors.black)
  term.clear()
  term.setCursorPos(1, 1)
  shell.run("/loc/browser/pages/webDis", file, url)
  term.redirect(term.native())
  paintutils.drawLine(6, 1, 50, 1, colors.white)
  term.setTextColor(colors.black)
  topBar()
end
-- Modem Message Function --
function modemMes(msg)
IP = inzernet.lookup(msg)
if IP == "error" then
  display(dir.."pages/error-DNS", "error-DNS")
else
  page = inzernet.GET(IP, "index")
  if page == "error" then

  else
	local web = fs.open(dir.."cache/izt-"..msg, "w")
	web.write(page)
	web.close()
	display(dir.."cache/izt-"..msg, msg)
  end
end
end
-- Load Settings --
function sLoad()
  local file = fs.open(dir.."settings", "r")
  local data = file.readAll()
  file.close()
  settings = textutils.unserialize(data)
end
-- Save Settings --
function sSave(table)
  local file = fs.open(dir.."settings", "w")
  file.write(textutils.serialize(table))
  file.close()
end
-- Main Loop --
function mainLoop(RF)
  while true do
  event = {os.pullEvent()}
	if event[1] == "modem_message" then
	  local side = event[2]
	  local freq = event[3]
	  local rfreq = event[4]
	  local msg = event[5]
	  local dis = event[6]
	  modemMes(msg, RF)
	elseif event[1] == "mouse_click" then
	  local button = event[2]
	  local x = event[3]
	  local y = event[4]
	  if x==51 and y==1 then
	  term.setCursorPos(1, 1)
		term.setBackgroundColor(colors.black)
		term.clear()
		break
	  end
	end
  end
end
-- Startup --
if fs.exists("/loc/browser/settings") then
  sLoad()
	parallel.waitForAll(drawMain(freq), mainLoop(freq))
else
  write("What side is your main modem on? [top, bottom, right, left, back, front] ")
  modem = read()
  write("What side is your network modem on? [na] for none [top, bottom, right, left, back, front] ")
  nmodem = read()
  write("Do you have an update server? [yes, no] ")
  updateServer = read()
  write("If [yes] what update type? If [no] put [na] ")
  updateType = read()
  tSettings = {
	mainSide = modem,
	networkSide = nmodem,
	updateServer = updateServer,
	updateType = updateType,
  }
  sSave(tSettings)
end

webDis code:

local arg = { ... }

os.loadAPI("/loc/browser/includes/inzernet")

os.loadAPI(arg[1])


inzernet API code:

-- Variables --
ID = os.getComputerID()
settings = {}
msg = ""
-- Lookup --
function lookup(website)
  pkt = {"lookup", website, "DNS", "DNS", "na", ID}
  sendPacket(pkt)
  return(msg[2])
end
function GET(IP, page)
  gIP = parser(IP)
  if tonumber(gIP[1]) then
	 pkt = {"GET", page, gIP[1], gIP[2], "na", ID}
	 sendPacket(pkt)
  else
	 msg[2] = "error"
  end
  return(msg[2])
end
function PING(IP)
  pIP = parser(IP)
  if tonumber(pIP[1]) then
	pkt = {"PING", "na", pIP[1], pIP[2], "na", ID}
	sendPacket(pkt)
  else
	msg[2] = "error"
  end
  return(msg[2])
end
function otherPacket(packetName, data, desRouter, desClient)
  pkt = {packetName, data, desRouter, desClient, "na", os.getComputerID }
  sendPacket(pkt)
end
function wait()
local timeout = os.startTimer(3)
  while true do
  event = {os.pullEvent()}
	if event[1] == "modem_message" then
	  local data = event[5]
	  return(data)
	elseif event[1] == "timer" and event[2] == timeout then
	  local data = "timeout"
	  return(data)
	end
  end
end
	
-- IP Parser --
function parser(ip)
  line = {}
	for word in string.gmatch(ip, "[^.]+") do
	  table.insert(line, word)
	end
return line
end
	
function sendPacket(pkt)
  main.transmit(100, 100, pkt)
  msg = wait()
end
function version()
  ver = {protocol = "1.0", API = "1.0"}
  return(ver)
end
function sLoad()
  local file = fs.open("/loc/browser/settings", "r")
  settings = textutils.unserialize(file.readAll())
  file.close()
end
local function sSave(table)
  local file = fs.open("settings","w")
  file.write(textutils.serialize(table))
  file.close()
end
-- Startup --
function startup()
  sLoad()
  main = peripheral.wrap(settings.mainSide)
  main.open(100)
end

At line 64 I get the error:
inzernet:64: attempt to index ? (a nil value)

I think this is because the API isn't being unloded, but I'm not sure. Any help and code to fix it is much appreciated!
DannySMc #2
Posted 05 November 2014 - 11:20 PM
When does this error occur? When you try to load the inzernet api? or?
Bomb Bloke #3
Posted 05 November 2014 - 11:27 PM
The 64th line of the inzernet API can't throw that error; is the "main browser code" also called "inzernet"? If so, then that'd suggest it doesn't like your attempt to open your settings file for read access. Maybe get it to print the path of the file it's trying to open (along with whether it thinks it exists or not), see what that tells you.
DannySMc #4
Posted 05 November 2014 - 11:30 PM
The 64th line of the inzernet API can't throw that error; is the "main browser code" also called "inzernet"? If so, then that'd suggest it doesn't like your attempt to open your settings file for read access. Maybe get it to print the path of the file it's trying to open (along with whether it thinks it exists or not), see what that tells you.

At the moment from testing, the table from the api line 64 is trying to return this: {protocol = "1.0", API = "1.0"}
according to my lua emulator this has an invalid index, now not sure if this is correct or not, but to fix it I changed it to this:


{[protocol] = "1.0", [API] = "1.0"}

This works? Try that in the API?
Dragon53535 #5
Posted 06 November 2014 - 12:03 AM
No that's not correct, if it's trying to index a nil value, it's trying to grab stuff from a table, that the table itself doesn't exist. perhaps remove the () from the return?

Also that way to define keys in a table is wrong. Or at least wrong if you don't already have a variable set to that. The two ways to make a constant key in a table.

{protocol = "1.0",["API"] = "1.0"}
Unless you already have protocol and API set to a string variable then that way won't work. (such as)

local s = "key"
{[s] = "value"}
lifewcody #6
Posted 06 November 2014 - 03:07 AM
When does this error occur? When you try to load the inzernet api? or?
The 64th line of the inzernet API can't throw that error; is the "main browser code" also called "inzernet"? If so, then that'd suggest it doesn't like your attempt to open your settings file for read access. Maybe get it to print the path of the file it's trying to open (along with whether it thinks it exists or not), see what that tells you.
No that's not correct, if it's trying to index a nil value, it's trying to grab stuff from a table, that the table itself doesn't exist. perhaps remove the () from the return? Also that way to define keys in a table is wrong. Or at least wrong if you don't already have a variable set to that. The two ways to make a constant key in a table.
 {protocol = "1.0",["API"] = "1.0"} 
Unless you already have protocol and API set to a string variable then that way won't work. (such as)
 local s = "key" {[s] = "value"} 

What I have is the API to have this:


var = inzernet.version()
print(var.protocol)

– edit –
Narrowed the problem to this line:

main.transmit(100, 100, pkt)
Edited on 06 November 2014 - 02:13 AM
Exerro #7
Posted 06 November 2014 - 10:09 AM
Something else you might have missed:

for word in string.gmatch(ip, "[^.]+") do -- will never find anything

for word in string.gmatch( ip, "([^%.]+)" ) do
You need the brackets because it's a capture, the brackets make it return the thing, and the %. because just a . is a special character repreesnting any character, and you're searching for the opposite of that.
Another thing is the paths in sLoad and sSave are different, maybe that is messing something up somewhere.

If it's erroring the second time, it means something is different to the first time. This is probably because of a settings file you have saved, or a global variable you have messed up.

Because it's erroring on that line, the error message shows that main is nil. So try and trace it back from there… why would main be nil? Because you haven't defined it yet. So why hasn't it been defined? Where is calling that function in the main code?

I believe it is the lookup function that is causing this problem. You are calling sendPacket from in there, and I don't see anywhere where you define the modem.
Edited on 06 November 2014 - 09:10 AM
lifewcody #8
Posted 07 November 2014 - 02:29 AM
Something else you might have missed:

for word in string.gmatch(ip, "[^.]+") do -- will never find anything

for word in string.gmatch( ip, "([^%.]+)" ) do
You need the brackets because it's a capture, the brackets make it return the thing, and the %. because just a . is a special character repreesnting any character, and you're searching for the opposite of that.
Another thing is the paths in sLoad and sSave are different, maybe that is messing something up somewhere.

If it's erroring the second time, it means something is different to the first time. This is probably because of a settings file you have saved, or a global variable you have messed up.

Because it's erroring on that line, the error message shows that main is nil. So try and trace it back from there… why would main be nil? Because you haven't defined it yet. So why hasn't it been defined? Where is calling that function in the main code?

I believe it is the lookup function that is causing this problem. You are calling sendPacket from in there, and I don't see anywhere where you define the modem.

The original code works fine and does return the desired value,

sSave has a different path because it is never called (yet) and doesn't work

and the modem is called in the startup function:

function startup()
  sLoad()
  main = peripheral.wrap(settings.mainSide)
  main.open(100)
end
Bomb Bloke #9
Posted 07 November 2014 - 02:44 AM
More info, dude!

Again, please be specific about which line in which file is generating the error. Is it your main script file, or your API?
lifewcody #10
Posted 07 November 2014 - 03:01 AM
When does this error occur? When you try to load the inzernet api? or?
The 64th line of the inzernet API can't throw that error; is the "main browser code" also called "inzernet"? If so, then that'd suggest it doesn't like your attempt to open your settings file for read access. Maybe get it to print the path of the file it's trying to open (along with whether it thinks it exists or not), see what that tells you.
No that's not correct, if it's trying to index a nil value, it's trying to grab stuff from a table, that the table itself doesn't exist. perhaps remove the () from the return? Also that way to define keys in a table is wrong. Or at least wrong if you don't already have a variable set to that. The two ways to make a constant key in a table.
 {protocol = "1.0",["API"] = "1.0"} 
Unless you already have protocol and API set to a string variable then that way won't work. (such as)
 local s = "key" {[s] = "value"} 

What I have is the API to have this:


var = inzernet.version()
print(var.protocol)

– edit –
Narrowed the problem to this line:

main.transmit(100, 100, pkt)

As I said, line 64 in the API inzernet:

main.transmit(100, 100, pkt)
Bomb Bloke #11
Posted 07 November 2014 - 04:16 AM
That's the 59th line of the version of the API you've shown us. What gives?

But assuming that IS the line the error is pointing to, then it's saying "main" is nil. This doesn't make sense, given that you wrapped a peripheral against it when you called inzernet.startup() - and that must've worked, or that function would've errored when it attempted to call main.open().
lifewcody #12
Posted 07 November 2014 - 03:55 PM
That's the 59th line of the version of the API you've shown us. What gives?

But assuming that IS the line the error is pointing to, then it's saying "main" is nil. This doesn't make sense, given that you wrapped a peripheral against it when you called inzernet.startup() - and that must've worked, or that function would've errored when it attempted to call main.open().

Oh, well the computer said it was the 64th line, but I added a print before and after the main.tramsit line and it only printed the text before main.transmit

And I know, I call inzernet.startup() so I know it is loading that, but would calling the startup function more than once cause the problem?
Bomb Bloke #13
Posted 08 November 2014 - 12:22 AM
Calling startup() multiple times shouldn't be a problem, at least, not in itself. But since you mention it, I see the problem now.

When you start your browser code, it loads the inzernet API. This generates a table holding the API's functions and variables, and the pointer to that table is stored against a global "inzernet" variable. Then you call inzernet.startup() - until that point, inzernet.main doesn't exist. After that point, inzernet.main is holding a pointer to the table with your wrapped modem's functions.

But sooner or later you run webDis and that reloads the inzernet API. The previous inzernet table gets wiped, and a new one gets generated. inzernet.main goes out the window - the new inzernet table won't have it until you call the new inzernet.startup() function. Hence when you attempt to call a function from inside the main table, you get an attempt to index nil - as there's no such table anymore.

Long story short, don't try to load the API more than once.
lifewcody #14
Posted 08 November 2014 - 12:34 AM
Calling startup() multiple times shouldn't be a problem, at least, not in itself. But since you mention it, I see the problem now.

When you start your browser code, it loads the inzernet API. This generates a table holding the API's functions and variables, and the pointer to that table is stored against a global "inzernet" variable. Then you call inzernet.startup() - until that point, inzernet.main doesn't exist. After that point, inzernet.main is holding a pointer to the table with your wrapped modem's functions.

But sooner or later you run webDis and that reloads the inzernet API. The previous inzernet table gets wiped, and a new one gets generated. inzernet.main goes out the window - the new inzernet table won't have it until you call the new inzernet.startup() function. Hence when you attempt to call a function from inside the main table, you get an attempt to index nil - as there's no such table anymore.

Long story short, don't try to load the API more than once.

Well I have webDis which loads the API again, so it's not that I don't call the startup to generate main again.
Dragon53535 #15
Posted 08 November 2014 - 12:45 AM
I think i know the reason to it not working a second time.


parallel.waitForAll(drawMain(freq), mainLoop(freq))
You are sending the value returned from the function as the arguments for waitForAll. If you want to run them without arguments you want to use

parallel.waitForAll(drawMain,mainLoop)
If you want to run them with arguments just create a dummy function to run them with the arguments.

local function startDM()
  drawMain(freq)
end
local function startML()
  mainLoop(freq)
end
parallel.waitForAll(startDM,startML)
Bomb Bloke #16
Posted 08 November 2014 - 12:46 AM
Well I have webDis which loads the API again, so it's not that I don't call the startup to generate main again.

Actually, it is specifically the case that you only call inzernet.startup() once, despite loading the API more than once.

Don't load the API more than once.
Edited on 07 November 2014 - 11:47 PM
lifewcody #17
Posted 08 November 2014 - 01:44 AM
Well I have webDis which loads the API again, so it's not that I don't call the startup to generate main again.

Actually, it is specifically the case that you only call inzernet.startup() once, despite loading the API more than once.

Don't load the API more than once.

How would I not load the API more than once? Right now I have it so before the web page is displayed, the InZernet API is loaded and so is the actual page. So it calls it more than once, how would I check to see if its loaded?
Bomb Bloke #18
Posted 08 November 2014 - 01:55 AM
Given that webDis is executed by your main browser code, and that main browser code starts off by loading the inzernet API, from where I'm sitting it seems that it's simply a case of rigging webDis not to do so.

But let's say you specifically wanted to check at a certain point in your code whether or not it's loaded, and load it if not. You'd just do this:

if not inzernet then os.loadAPI("/loc/browser/includes/inzernet") end
Edited on 08 November 2014 - 12:55 AM
lifewcody #19
Posted 08 November 2014 - 04:14 AM
Given that webDis is executed by your main browser code, and that main browser code starts off by loading the inzernet API, from where I'm sitting it seems that it's simply a case of rigging webDis not to do so.

But let's say you specifically wanted to check at a certain point in your code whether or not it's loaded, and load it if not. You'd just do this:

if not inzernet then os.loadAPI("/loc/browser/includes/inzernet") end

Ok thanks, also what would be a replacement for shell.run in an API?
theoriginalbit #20
Posted 08 November 2014 - 04:18 AM
well really you should be avoiding running other programs in most cases, let alone in an API, however to answer your question, os.run would be appropriate, as that is what shell.run makes use of
lifewcody #21
Posted 08 November 2014 - 04:45 AM
well really you should be avoiding running other programs in most cases, let alone in an API, however to answer your question, os.run would be appropriate, as that is what shell.run makes use of

Thanks! It is working now (I used Dragon5353's code and BombBlake's code, thanks guys!!), but now at the top of the screen (below the URL bar) it does not get cleared. Any ideas?