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

[solved] Need help transitioning from one function to another

Started by Nhorr, 06 February 2016 - 07:50 PM
Nhorr #1
Posted 06 February 2016 - 08:50 PM
I'm back again… this time not understanding what exactly is the issue with the following actions in my UI:
  • the updater() function (in desktop) doesn't load the actual updater
  • going back in the uninstaller menu simply breaks rather than going back (tried various things - still can't get all of the desktop to display again and drawDesktop() freezes button selection)
  • passwordSetup() is also not working, like updater()
What I want to happen is when updater() runs, it loads the function of the downloaded updater program, listing out different versions to choose to install. When going back to desktop, it's supposed to go back to the menu seen previously (the one the computer starts with). The uninstallCheck() function runs fine if selecting yes, but returning to desktop doesn't work and I can't seem to find out how. passwordSetup(), like updater, gives a blank screen, that when a spacebar or number is pressed it shows half of the desktop screen (menu and non-self-updating clock), but it (unlike the updater) is built into the desktop program.

The UI installer (to see what I'm talking about):
http://pastebin.com/qExuNAUg
pastebin run qExuNAUg

The 'desktop' program (source of these issues - I left a few comments at the top for myself you can read):
http://pastebin.com/JUhsDLDd

I'll be glad to answer any questions about the programs/functions - I know I'm not really being clear, but it's easier to understand with use of the UI.
Any other all-round tips are always appreciated; after all, one only succeeds at getting better at something if they practice and listen to advice.

Many, many thanks in advance!
Edited on 11 February 2016 - 07:32 PM
Bomb Bloke #2
Posted 07 February 2016 - 01:11 PM
the updater() function (in desktop) doesn't load the actual updater
passwordSetup() is also not working, like updater()

Because of pingPastebin(). It sends an HTTP request, then checks the first event in the event queue. Odds are that'll be a timer event from the sleep() call you're making in your drawDesktop() function, so "internet" gets set to false and not much else happens. Don't set "requesting" to false until you're sure you've got the event you want.

If you're really quick off the mark you can get in before the timer event as the code is, but that'll crash because the updater script attempts to call resetScreen(). This function is defined in the global scope by your desktop script, but APIs don't have access to that scope; only to _G. All "user-mode scripts" use a different table for their global scope, so if you want to use functions across multiple scripts, best to load them up as proper APIs.

going back in the uninstaller menu simply breaks rather than going back (tried various things - still can't get all of the desktop to display again and drawDesktop() freezes button selection)

Obviously you need to re-run the code that draws the desktop if you want the desktop to appear again, but currently, calling the drawDesktop() function involves starting a loop that repeats so long as disptime ~= 0, so it's not much good for simply drawing the desktop! I suggest you make a separate function for updating the time display.

Any other all-round tips are always appreciated; after all, one only succeeds at getting better at something if they practice and listen to advice.

Use the keys API, it's filled with lots of lovely constants to make your script more readable! For example, this:

if b==2 then uCYes()

… becomes this:

if b==keys.one then uCYes()

Rather than having a ton of duplicate cases in your runDesktop() function, how about using "or":

    if (event=="key" and sel==0 and b==2) or (event=="mouse_click" and b==1 and x>=2 and x<=8 and y==1) then sel=1
        rDMenu()

Personally, I've taken to relying on these two functions:

-- Returns whether a click was performed at a given location.
-- If one parameter is passed, it checks to see if y is [1].
-- If two parameters are passed, it checks to see if x is [1] and y is [2].
-- If three parameters are passed, it checks to see if x is between [1]/[2] (non-inclusive) and y is [3].
-- If four paramaters are passed, it checks to see if x is between [1]/[2] and y is between [3]/[4] (non-inclusive).
local function clickedAt(...)
        if myEvent[1] ~= "mouse_click" then return false end
        if #arg == 1 then return (arg[1] == myEvent[4])
        elseif #arg == 2 then return (myEvent[3] == arg[1] and myEvent[4] == arg[2])
        elseif #arg == 3 then return (myEvent[3] > arg[1] and myEvent[3] < arg[2] and myEvent[4] == arg[3])
        else return (myEvent[3] > arg[1] and myEvent[3] < arg[2] and myEvent[4] > arg[3] and myEvent[4] < arg[4]) end
end

-- Returns whether one of a given set of keys was pressed.
local function pressedKey(...)
        if myEvent[1] ~= "key" then return false end
        for i=1,#arg do if arg[i] == myEvent[2] then return true end end
        return false
end

With myEvent localised to the whole script, and populated as a table:

myEvent = {os.pullEvent()}

… I can then do compact checks like so:

    if (pressedKey(keys.one, keys.numPad1) and sel==0) or clickedAt(1, 9, 1) then sel=1
        rDMenu()

The ternary technique discussed here lets you turn this sort of thing:

  if term.isColor() then term.write("(Use mouse &amp; keyboard for selecting.)")
  else term.write("(Use keyboard for selecting.)")
  end

… into this sort of thing:

  term.write("(Use " .. (term.isColor() and "mouse &amp; " or "") .. "keyboard for selecting.)")
Nhorr #3
Posted 07 February 2016 - 01:31 PM
Thanks once again for your feedback, Bomb Bloke! I'll be looking into my code with these things in mind soon - will give results :)/>
Edited on 07 February 2016 - 12:31 PM
Nhorr #4
Posted 09 February 2016 - 09:00 PM
Alright, so I've done a some changes to my code.

the updater() function (in desktop) doesn't load the actual updater
passwordSetup() is also not working, like updater()

Because of pingPastebin(). It sends an HTTP request, then checks the first event in the event queue. Odds are that'll be a timer event from the sleep() call you're making in your drawDesktop() function, so "internet" gets set to false and not much else happens. Don't set "requesting" to false until you're sure you've got the event you want.

If you're really quick off the mark you can get in before the timer event as the code is, but that'll crash because the updater script attempts to call resetScreen(). This function is defined in the global scope by your desktop script, but APIs don't have access to that scope; only to _G. All "user-mode scripts" use a different table for their global scope, so if you want to use functions across multiple scripts, best to load them up as proper APIs.

So I made a few changes, originally putting the 'requesting=false' lines at the end of the executables, but then I remembered something I should have explained first: pingPastebin()'s sole purpose is to, well, ping pastebin.com's servers to check for a connection. It's not actually drawing in any information besides determining if whether or not 'internet' = true/false before proceeding to run the actual function that called pingPastebin() based on the value of 'internet.'

I know pingPastebin() might be a crude function for trying to determine if there's an internet connection, so if something simpler exists I'd love to know - haven't really found another way that I understood how to do.

going back in the uninstaller menu simply breaks rather than going back (tried various things - still can't get all of the desktop to display again and drawDesktop() freezes button selection)

Obviously you need to re-run the code that draws the desktop if you want the desktop to appear again, but currently, calling the drawDesktop() function involves starting a loop that repeats so long as disptime ~= 0, so it's not much good for simply drawing the desktop! I suggest you make a separate function for updating the time display.

Done. displayTime() is now its own thing, currently unincorporated in anything to try to narrow down the issue.
drawDesktop() is now independent. drawDesktop() don't need no displayTime().

Besides that, your clickedAt() and pressedKey() functions are really impressive (at least to me, where I'm at lua knowledge-wise) - thanks for sharing them with me. I've incorporated it into my code, and modeled some things after the tips you gave - I know I'm not the only one that loves making their code not only shorter but more readable. Only problem now is, I can't seem to select anything. Gonna try using non-fancy button inputs and see what that yields.

EDIT: not even with lines like:
if (event=="key" and s==0 and b==keys.one) or (event=="mouse_click" and b==1 and x>=2 and x<=8 and y==1) then s=1
does it do any selecting on a basic computer. Same if on the key side b is set to == 2.
On an advanced computer the menus drop down, but reset before anything can be clicked. I think this might be an issue with setting variable 's' (selection rule).
Desktop is drawing, so I'm guessing it's an issue with runDesktop() but I just can't tell what the issue is.
Edited on 10 February 2016 - 11:42 AM
Bomb Bloke #5
Posted 10 February 2016 - 03:03 AM
Line 211:

    else s=0 runMenu() end

As with the pingPastebin() function (that you've now fixed), the problem here is that you're running this bit of code when you don't want to. A timer event will trigger it, and ditto for mouse_up / key_up events.

Change it to something like:

    elseif event == "key" or event == "mouse_click" then s=0 runMenu() end
Nhorr #6
Posted 10 February 2016 - 05:10 PM
Line 211:
 else s=0 runMenu() end
As with the pingPastebin() function (that you've now fixed), the problem here is that you're running this bit of code when you don't want to. A timer event will trigger it, and ditto for mouse_up / key_up events. Change it to something like:
 elseif event == "key" or event == "mouse_click" then s=0 runMenu() end

Oh…! *facepalm* …gotcha… I made the change. Won't be able to test it till later though.

Also, I have one other question (if you don't mind me asking) - I'm still trying to understand function parameters and wondering if the following would work: if I set runMenu() to runMenu(s) (as well as the functions it runs [menuBar() and drawMenus()] to have an 's' in the parenthesis as well for the selection rule variable), could I then say in the desktop structuring
if (event=="key" and s==0 and b==keys.one) or (event=="mouse_click" and b==1 and x>=2 and x<=8 and y==1) then runMenu(1)
in place of
if (event=="key" and s==0 and b==keys.one) or (event=="mouse_click" and b==1 and x>=2 and x<=8 and y==1) then s=1 runMenu()
and still have the functions respond to regular declarations of the variable 's' as well as set the value of 's' itself?

Like, if I said:
var=1
local function checkAndSetVar(var)
  print(var==1 and "var is 1" or "var is not 1")
  var=var+1
end
and ran the function checkAndSetVar(var) twice, would it yield:
var is 1
var is not 1
?

By the way, I really do want to sincerely thank you BB for all the help you've been in all you've helped me in my various topics - NhUI has been just a personal project for fun and I've still got plenty to learn about coding, but you've been an amazing help as well as others in this section of the forum to help me both with my code and my overall understanding with coding. Really grateful for all of you.
Edited on 10 February 2016 - 06:02 PM
Bomb Bloke #7
Posted 10 February 2016 - 11:59 PM
… and ran the function checkAndSetVar(var) twice, would it yield:
var is 1
var is not 1
?

No, it'd print "var is 1" both times.

When the checkAndSetVar() function is executed, it generates a new variable that's localised to that function called "var" (because you stuck "var" in the argument list within the function definition statement). All references to "var" inside that function will point to that local variable, regardless as to whether a "var" variable exists within a higher scope. That local variable is simply discarded from memory when the function ends (if you wanted to retain it, you'd either stick with using upvalues - as your earlier script does - or have the function return the value of the function before it ends).

Much the same deal with "for" loops: the counter variables are localised to the loop. Eg:

for i = 1, 3 do
  for i = 1, 5 do
    print(i)
  end
end

… prints the numbers 1 to 5 three times. The inner loop doesn't confuse its localised "i" with the outer loop's.

If you want to test code snippets and don't have immediate access to Minecraft, remember there's always repl.it and Mimic. The behaviour isn't always exactly identical to what you'll see in ComputerCraft, but at least in terms of syntax and the basic rules of the Lua language, they're all the same.
Edited on 10 February 2016 - 11:02 PM
Nhorr #8
Posted 11 February 2016 - 12:36 PM
EDIT: I'm 99% sure I found the issue, don't bother trying to answer my question in this reply. I'll test the fix on the actual emulator this afternoon.

… and ran the function checkAndSetVar(var) twice, would it yield:
var is 1
var is not 1
?

No, it'd print "var is 1" both times.

When the checkAndSetVar() function is executed, it generates a new variable that's localised to that function called "var" (because you stuck "var" in the argument list within the function definition statement). All references to "var" inside that function will point to that local variable, regardless as to whether a "var" variable exists within a higher scope. That local variable is simply discarded from memory when the function ends (if you wanted to retain it, you'd either stick with using upvalues - as your earlier script does - or have the function return the value of the function before it ends).

Much the same deal with "for" loops: the counter variables are localised to the loop. Eg:

for i = 1, 3 do
  for i = 1, 5 do
	print(i)
  end
end

… prints the numbers 1 to 5 three times. The inner loop doesn't confuse its localised "i" with the outer loop's.

Ah, ok, got it. Wrote up a few functions to test this out and I see what you're saying.

If you want to test code snippets and don't have immediate access to Minecraft, remember there's always repl.it and Mimic. The behaviour isn't always exactly identical to what you'll see in ComputerCraft, but at least in terms of syntax and the basic rules of the Lua language, they're all the same.

Thanks for that - just used repl.it to test the just-mentioned functions and it worked pretty well, thanks!

Alright, well, testing this code (http://pastebin.com/thHTZvCQ) gives good results, and menus/programs are appearing/etc so the original problem seems fixed or at least at a point where I could get more leverage into the actual problem if one remains. I am a little bummed, however, that I can't seem to get this version (http://pastebin.com/JUhsDLDd) that uses your functions for clicking or button pressing to work. I could just as well use the first version, but I'm curious: anything I'm doing wrong using your code in mine?

Thanks again for all the help, this is likely my final question.
Edited on 11 February 2016 - 01:38 PM