Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal

mpmxyz

Members
  • Content Count

    56
  • Joined

  • Last visited

  • Days Won

    24

Posts posted by mpmxyz

  1. Content

    • Introduction
    • Reproducing an Error
    • Error Messages
    • Debug Output
    • Appendix
      • List of Error Messages
      • Using a Debugger
      • Ad alert: cbrowse

    Introduction
    Writing code is one thing, making it work is a completely different story. More often than not something is not working as intended and sometimes your program just crashes the instant you run it.
    This can be fun and inspiring but most often it is just frustrating.
    This guide is meant to introduce you to several useful debugging techniques with the primary focus being OpenComputers and Lua.

    I could use your help to improve this guide. If you spot an error or miss some content: Please tell me!

    Reproducing an Error
    Even with maxed out debugging skills you are going to have a hard time fixing a bug if you can't see it.
    So step one in debugging is finding a way to reproduce the bug. This is something that does not necessarily require programming.

    You can even do that as a user!

    It is very good manners to add a step by step instruction to your bug report:
    Write down every step of what you did to reach the bug. Test this instruction yourself and try to find ways to make it as short as possible.
      Maybe the work you did before opening *this* file did not have anything to do with your bug.
    Also try some variations to find out if this bug needs a specific setup or if it happens in a more general use case.
      If the program had no trouble on a T3 screen but crashes on a T1 screen there might be something involving the resolution or color depth. Does it work with a T2 screen?
    This makes it very easy to see the bug in action. Even if it is just 1 in 10 times. It helps.
      Beware of the Heisenbug which only appears when you are not watching it! ;-)

    Error Messages

     


    This is a program that should print 4 numbers:

    
    function draw(v, ...)
      if v then
        print(("%i"):format(v))
        draw(...)
      end
    end
    draw(1, 2, 3, {4.5})

    It gives you normal output...

    
    1
    2
    3

    ...and this error message:

    
    /test.lua:3: bad argument #1 to 'format' (number expected, got table):
    stack traceback:
    	[C]: in function 'format'
    	/test.lua:3: in function 'draw'
    	/test.lua:4: in function 'draw'
    	/test.lua:4: in function 'draw'
    	/test.lua:4: in function 'draw'
    	/test.lua:7: in main chunk
    	(...tail calls...)
    	[C]: in function 'xpcall'
    	machine:751: in function 'xpcall'
    	/lib/process.lua:84: in function </lib/process.lua:80>

    The first line is enough for most bugs.
    It contains the location where the error message comes from:
      /test.lua:3 - file /test.lua, third line
    It also contains the error reason:
      bad argument #1 to 'format' (number expected, got table) - You called the function 'format' but the type of its first argument - 'v' - was 'table' and not 'number' as the function expected.
      v shouldn't be a table. Where does it come from?
    To understand that it might be good to read the stack traceback.
    The stack is a part of the memory Lua uses to remember where to jump back to after returning from a function. (It also stores local functions.)
    A traceback is a human readable printout of this data. *You need to read it from the bottom to the top.*

    
    [C]: in function 'format'
    	The value is prepared within the C function 'format'. This function throws the error because it detected the wrong type of its parameters. *You can continue to read left->right/top->bottom.*
    /test.lua:3: in function 'draw'
    	This is the line where the (originally) fourth value is supposed to be printed. *move up*
    /test.lua:4: in function 'draw'
    	It is a very powerful concept in programming. *move up*
    /test.lua:4: in function 'draw'
    	This process of solving part of the problem and calling itself to solve the remaining problem is called 'recursion'. *move up*
    /test.lua:4: in function 'draw'
    	'draw' which displays its first argument and calls itself with the remaining arguments. *move up*
    /test.lua:7: in main chunk
    	This is the point where our file has been executed. In line 7 we called function... *move up*
    (...tail calls...)
    	Lua optimizes so called 'tail calls'. You don't get additional information. *move up*
    [C]: in function 'xpcall'
    	it called a function also called 'xpcall'. But this one is written in C and not in Lua. (Therefore we don't get much info.) Of course this function also calls other functions. *move up*
    machine:751: in function 'xpcall'
    	It called the named function 'xpcall' defined in the builtin file 'machine'. In line 751.... *move up*
    /lib/process.lua:84: in function </lib/process.lua:80>
    	This is where it all started. The process library of OpenOS called a function without name created in file /lib/process.lua, line 80. In line 84 it called another function. *move up*

    It is possible to get a traceback without an error by running debug.traceback() within Lua.

     

    Debug Output

     


    Staring at the code and error message didn't solve the problem. We still don't know where this value really came from.
    Next step is adding debug output. For simple things you can use print. For more complicated projects it might be a good idea to write to a file or even create a whole library around it.
    This is the updated program:

    
    function draw(v, ...)
      print(v) --debug output
      if v then
        print(("%i"):format(v)) --normal output
        draw(...)
      end
    end
    draw(1, 2, 3, {4.5})

    This is the output:

    
    1					<-debug output
    1					<-normal output
    2
    2
    3
    3
    table: 0x689d10
    

    v indeed is a table on the fourth call before it is used in function 'format'.

    For complete information you should print the other function arguments, too.
    With so many numbers being thrown around its best if you add the name of each value and describe what is happening:

    
    function draw(v, ...)
      print("begin draw")
      print("v=", v)    --debug output
      print("...=", ...) --debug output
      print("#...=", select("#", ...))
      if v then
        print(("%i"):format(v))
        draw(...)
      end
      print("end draw")
    end
    print("before draw")
    draw(1, 2, 3, {4.5})
    print("after draw")

    The output:

    
    before draw
    begin draw
    v=	1
    ...=	2	3	table: 0xab54d0
    #...=	3
    1
    begin draw
    v=	2
    ...=	3	table: 0xab54d0
    #...=	2
    2
    begin draw
    v=	3
    ...=	table: 0xab54d0
    #...=	1
    3
    begin draw
    v=	table: 0xab54d0
    ...=
    #...=	0

    Here we can see that the table is already part of the four original arguments. We should better check the line where 'draw' is initially called:

    
    draw(1, 2, 3, {4.5})

    That is easily fixed:

    
    draw(1, 2, 3, 4.5)

    Now the output is:

    
    before draw
    begin draw
    v=	1
    ...=	2	3	4.5
    #...=	3
    1
    begin draw
    v=	2
    ...=	3	4.5
    #...=	2
    2
    begin draw
    v=	3
    ...=	4.5
    #...=	1
    3
    begin draw
    v=	4.5
    ...=
    #...=	0
    4
    begin draw
    v=	nil
    ...=
    #...=	0
    end draw
    end draw
    end draw
    end draw
    end draw
    after draw

    Looks good, doesn't it? Let's remove the debug output again:

    
    function draw(v, ...)
      if v then
        print(("%i"):format(v))
        draw(...)
      end
    end
    draw(1, 2, 3, 4.5)

    Output:

    
    1
    2
    3
    4

    There is another bug. Can you spot it?

     

    4.5 is printed as 4.
    In the previously removed debug output we could see that the value of v is 4.5 but on the screen it is 4.
    There is either a formatting error or print() is not printing everything.
    assert() can help here: When the first argument is true, it does nothing, else it throws an error with its second argument as a message.
    So we can split line 3 in two parts and add a test in the middle which tells us if the formatting code works as intended.

    
    
    function draw(v, ...)
      if v then
        local text = ("%i"):format(v)
        assert(tonumber(text) == v, "Formatting didn't work!")
        print(text)
        draw(...)
      end
    end
    draw(1, 2, 3, 4.5)

    Output + Error Message:

    
    
    1
    2
    3
    /test.lua:4: Formatting didn't work!

    It is a problem with formatting. The format is "%i" which is only outputting integer numbers. One could use "%f" instead to allow for floats.
    Now everything works. You can remove the assert.

    It is good practice to write modular code and to avoid too long lines doing all at once.
    That way you can always squeeze in some debugging code.

    It isn't always practical to write print() in every corner of your program.
    If your bug originates from a single error you can use the binary search algorithm:

    1. The error could be anywhere from the beginning of the program to the point where you noticed it. (e.g. crash, invalid output, wrong redstone signal etc.)
    2. Add a test in the middle of the area of code that could contain the error.
    3. If the test is passed the error is in the following half. Otherwise it is in the half before the test.
    4. Unless you found the code that caused the error, you continue with step 2 but with a smaller area than before.

    Here is an example:
     

    
    
    function a(x)
      return x * 2
    end
    function b(x)
      return x * 3
    end
    function c(x)
      return x-x
    end
    function d(x)
      return -x
    end
    
    x = 15
    x = a(x)
    x = b(x)
    x = d(x)
    x = a(x)
    x = c(x)
    x = a(x)
    x = d(x)
    
    assert(x~=0, "x is never allowed to be 0")
    print(1 / x)

    The area of attention lies in this area:

    
    
    x = 15   --x It could be any line marked with an x.
    x = a(x) --x
    x = b(x) --x
    x = d(x) --x
    x = a(x) --x
    x = c(x) --x
    x = a(x) --x
    x = d(x) --x

    Insert a test in the middle:

    
    
    x = 15
    x = a(x)
    x = b(x)
    x = d(x)
    assert(x~=0, "It is the previous half.") --passed
    x = a(x) --x We halved the search area.
    x = c(x) --x
    x = a(x) --x
    x = d(x) --x

    And again:

    
    
    x = 15
    x = a(x)
    x = b(x)
    x = d(x)
    x = a(x) --x We halved the search area again.
    x = c(x) --x
    assert(x~=0, "It is the previous half.") --failed
    x = a(x)
    x = d(x)

    And one last time:

    
    
    x = 15
    x = a(x)
    x = b(x)
    x = d(x)
    x = a(x)
    assert(x~=0, "It is the previous half.") --passed
    x = c(x) --x This is the only line remaining. What is c doing?
    x = a(x)
    x = d(x)

    If c was a more complex function you could do another binary search over the whole function.
    In this example it is quite obvious that x-x results in a value of 0.

     

     

    Appendix

    List of error messages

    • Syntax Error: detected when loading code
      • syntax error near 'x'
      • 'x' expected near 'y'
      • 'a' expected (to close 'b' at line c) near 'c'
      • Most of the time it's just a missing 'end' or bracket. That's easy to see if you use indentation.
        Sometimes it is a bit harder to find, but b()=2 clearly is not correct.
      • <eof> means the end of file
    • Runtime Error: detected when the code is executed
      • Runtime errors aren't as easy. They can hide in rarely used code and there is a variety of them:
      • bad argument #1 to 'func' ('type' expected, got 'wrongType')
        • You called a function but one of the parameters you've given has the wrong type.
          You should check the functions documentation and compare to how you use it in your program.
      • attempt to index local/global/field 'name' (a 'type' value)
      • attempt to index a 'type' value
        • You tried to access a table field but there actually was no table you could access. (but nil, a number, a function etc.)
          Check the return value/variable you are using and work backwards to find out where a check has failed, the value has been overwritten or not written.
      • table index is NaN/nil
        • You tried to access a table using an invalid index. Everything is allowed to be an index except nil and NaN.
      • attempt to call local/global/field 'name' (a 'type' value)
      • attempt to call a 'type' value
        • The return value/variable you tried to call as a function hasn't been a function or a value with a __call metamethod.

    Using a Debugger
    In other environments it is possible to use an external program to help debugging, a 'debugger'.
    This would display and modify the values of variables and would allow to stop the program at previously defined breakpoints.
    After stopping it is possible to step through the program line by line to check what it is doing.
    Just to be clear: This is possible in normal Lua but OpenComputers already occupies the same feature to limit execution time.

    Ad Alert: cbrowse
    You can easily add advanced debug in/output by installing my program cbrowse.

    The debug interface works by calling this:

    local a, b, c = 1, "A", {test = true}
    require"cbrowse".view(a, b, c)

    Tables can be manipulated. If you want to change the value of a variable you have to temporarily use a table and manually export the result back to the local variable.

    local a = 1
    local _t = {a = a}
    require"cbrowse".view(_t)
    a = _t.a

    But there is one drawback: None of the events happening while cbrowse runs reach your event.pull in your program. Event listeners work though.

  2. I can't help you directly because I don't have OpenSecurity installed and you've already got too much code for me to walk through manually.
    But here are a few hints:

    • Add debug output! (A lot!) You want to find out the values of important variables throughout your code.
    • Compare the output to what you would expect!
    • You can narrow down the area of the error because it must be somewhere after a correct output and before a wrong output.
    • Move your outputs closer to the error.
    • When you found the error fix it.
    • You can now remove all remaining debug output.

    In this case this part of your controller is quite interesting:

     data = crypt(msg, cryptKey, true)
    --    print(data)
          if data == "true" then
        term.write("Access granted\n")

    A printout of msg, data and cryptKey in server and controller would be interesting. Since cryptKey is a table you can use serialization.serialize(cryptKey, true) to get a neat output.

    On a side note, here is how you shouldn't do debug output:

    for i = 1, 10 do
      print(i)
      local a = i * 2
      local b = i * a + 2
      print(a)
      a = a + 2
      print(a)
      print(b)
    end

    It is okay if you just check the value of one variable but it can confuse you a lot if you have long loops and many variables. (Which value belongs to which variable?)

    It is much better if you add names and descriptions to your output:

    for i = 1, 10 do
      print("new iteration i=",i)
      local a = i * 2
      local b = i * a + 2
      print("1 a=", a)
      a = a + 2
      print("2 a=", a)
      print("3 b=", b)
    end

    This way it is much easier to know which variable is being displayed and where the output comes from.

  3. Update 13.04.17:

    -fixed a bug which was caused by filesystem.list returning file names without sorting in newer OC versions
    -added crunch to oppm

    I decided not to add a 'self extracting archive' option because that deserves to be another program. The output of that program could be compressed though.

    My next target will be full Lua 5.3 support. I have to check memory requirements after that though. (lots of new operators)

    Update 16.04.17:

    -Added Lua 5.3 Support (Operators were missing.)

    Memory requirement didn't increase noticeably. But the parsing table grew by 12KiB. So there is some increase.

  4. It's actually a good idea to keep using this part as it allows for easier extension using options:

    local args, options = shell.parse(...)
    --"prog arg1 -ab --option --value=2 arg2 arg3" will result in:
    args = {"arg1", "arg2", "arg3"}
    options = {
      a = true,
      b = true,
      option = true,
      value = "2",
    }

    But this needs further processing because the received arguments are strings and the functions expect number values corresponding to a light number and a color:

    local shell = require("shell")
    local colors = require("colors")
    
    local args, options = shell.parse(...)
    
    (...)
    --You can access a table using "yourTable[yourIndex]".
    --'tonumber' converts a string value to a number. 0xABCD00 is just a hexadecimal number.
    ColorSet(tonumber(args[1]), tonumber(args[2]))

     

  5. If you have installed an internet card in your computer you can access the "real world" internet.

    One example on what you could do:

    wget 'https://raw.githubusercontent.com/mpmxyz/ocprograms/master/home/bin/tar.lua'
    

    This will download the file tar.lua. It is a small program I wrote to interact with real world *.tar archives.

    Note: On some OpenComputers versions you had to install internet related programs separately using an "internet disk".

  6. Disclaimer: I haven't thought through everything; it's just a quick idea.
    You'd have to hack into the component system and add a "virtual" gpu bound to a "virtual" screen.
    Both virtual components should be set to be the primary one.

    It involves a lot of writing code to support the basic functions of gpu and screen. Events have to be modified, too.

  7. Isn't there something like a speed upgrade for MAs in AE2? http://ae-mod.info/Acceleration-Card/

    If you want to forward a crafting request to OC you can use an AE2 interface to a buffer robot.

    You could have one pair for compressing tasks and another pair for uncompressing tasks.

    I could imagine having two columns of robots. The rear column is distributing the items and the front column is doing the crafting. The two robot columns would be controlled by broadcast wireless signals. The two (or more) input buffers would limit themselves to something like 1000 crafting actions and would give way to the next buffer to avoid one crafting request to block the other. This system is limited by the item transfer rate of a single robot though.

  8. Sometimes it is helpful to write a small tool.
    To give you an example - it requires you to remove the blocks adding components and rebuilding them in the right order:

    pastebin get LCXQ9JH1 factory_companion.lua
    factory_companion test.cfg
     [enter number of different components]
     [enter a field name for every component type, can be nested (i.e. "inventory", "furnace.right" or even an empty string to add a list to the root table)]
     [add the stated component to the network; the program listens to "component_added" signals]
     [press enter to save config file]
    cat test.cfg
    

    It's just a quick and dirty program. It could have been nicer.

    You can take it as an example for a tool of your own.

  9. You haven't seen the part about slavery, did you?

    For me it looks like a parody of real license agreements.

    PS: Nice UI, but I wished that it was fully translated... I just formatted the disk because I don't understand Russian at all. xD

    Do you already use a localization library? A well made one should make your live easier.

  10. Your program is nice. Processing the component documentation to make it more readable is a nice idea.

    I read through your code and noticed one thing:

    string.find(methodDoc, "[--]")

    When writing this you probably thought that this matches the two minus signs within a documentation string.

    But the square brackets indicate a set of possibilities for one character only. "%-%-" matches two minus signs.

    Since you adjusted the indices in the following string.sub calls there is no noticeable effect on your program. (So if you "correct" that string.find, you have to change the indices again.)

     

    What about a generic documentation for libraries?

    debug.getinfo can get you a lot of useful information. (Another reason to use good parameter names when writing your library. ;-))

     

    PS: Have you tried my component/library program? While it is able to display unmodified method documentation via tostring(method), it has a much larger focus on working with/trying out things. A split screen with both programs running in parallel would be perfect. (A multi-term-implementation is coming soon but built-in multitasking is still missing.)

  11. Introduction

     

    Are you creating the secret underground base? A pyramid maze with all kinds of traps? Or just your high security door lock?

    Then it's very likely that you are using redstone. More complex designs require bigger circuits or computers. (redstone computers?)

    Programming with redstone cards or blocks is quite straightforward, but it can be quite annoying to always repeat yourself:

    local component = require("component")
    local sides = require("sides")
    local colors = require("colors")
    local rs = component.redstone
    rs.setBundledOutput(sides.north, colors.blue, true)   --close entrance
    os.sleep(1)
    rs.setBundledOutput(sides.north, colors.green, false) --open exit
    os.sleep(10)
    rs.setBundledOutput(sides.north, colors.green, true)  --close exit
    os.sleep(1)
    rs.setBundledOutput(sides.north, colors.blue, false)  --open entrance
    

    This library makes things easier and your code more expressive:

    local component = require("component")
    local rstools = require("mpm.rstools")
    local rs = component.redstone
    --inverted to make true == open
    local entrance = rstools.digital("output", rs, "north", "blue").inverted
    local exit     = rstools.digital("output", rs, "north", "green").inverted
    entrance(false)             --close entrance
    os.sleep(1)
    exit:pulse(10, true, false) --open exit for 10 seconds
    os.sleep(1)
    entrance(true)              --open entrance
    

    Documentation

    rstools.analog(type, address/proxy, side/"wireless"[, color]) -> object
    rstools.digital(type, address/proxy, side/"wireless"[, color]) -> object
    
    Both functions need a type which is either the string "input" or "output".
    While "input" objects read the input value, you can use "output" objects to read and write output values.
    
    The second parameter defines the redstone component to be used. (either using its address or its proxy)
    
    The third parameter defines the side to be used. You can use the numeric value or the name of the side.
    Wireless redstone has the side "wireless".
    
    For bundled cables you can use the fourth parameter to specify the color. (numeric value or name)
    The color "all" makes the object accept and return tables of values.
    
    analog vs. digital
    "analog" objects work with numeric values. (e.g. 0 - 15 for vanilla redstone)
    "digital" objects work with boolean values. Wireless component functions only return and accept boolean values, so they are converted to 0 and math.huge.
    
    object() -> returns current value
    object.inverted -> an inverted object (true -> redstone off, false -> redstone on, only useful for digital objects)
    
    type == "output" only:
    object(value) -> changes output
    object:pulse(duration, value[, originalValue]) -> overrides the output for 'duration' seconds, then restores original value
    object:toggle() -> inverts the output (only useful for digital objects)
    

    Depencencies

     

    Software

    OpenOS

     

    Hardware

    Redstone Component

     

    Installation

     

    Download the file and save it as the library "mpm.rstools".

     

    Download (last update: 29.01.16)

     

    github

    mkdir /home/lib/mpm
    cd /home/lib/mpm
    wget 'https://raw.githubusercontent.com/mpmxyz/ocprograms/master/home/lib/mpm/rstools.lua'
    

    Known Issues

     

    None.

  12. Maybe you can use something like that:

    local event = require("event")
    local computer = require("computer")
    
    local TEST = tostring(...)
    
    local function move(...)
      print(TEST, ...) --your code
    end
    
    --This function removes the listener when the next program pushes the signal "update_listeners"
    local function removeListener()
      event.ignore("modem_message", move)
      --removes this function
      return false
    end
    --This function adds the listener.
    local function addListener()
      event.listen("modem_message", move)
      --The internal list of listeners is updated after all listeners have been executed.
      --That means that the following function will not be executed in this step:
      event.listen("update_listeners", removeListener)
      --removes this function
      return false
    end
    
    event.listen("update_listeners", addListener)
    computer.pushSignal("update_listeners")
    
  13. How many players are in your list "playerData.players"?

    You are using insertion sort to sort this list. This is quite slow for big lists. (time: n² with n as the size of the list, table.sort() uses a better sorting algorithm, n * log(n) )

    But unless you have a very big list of users it probably isn't the reason

     

    Another performance hint:

    You can process the list returned by od.getPlayerList() before you continue using it:

    local function updatePlayerData(curTime)
      local isOnlinePlayer = {} --This table is the key for speed improvements.
      for _, onlinePlayer in ipairs(od.getPlayerList()) do
        isOnlinePlayer[onlinePlayer] = true
      end
      --It allows you to take the 3 player loops out of the first loop.
      for _, v in pairs(playerData.owner) do
        --Instead of searching if a player is online you now get the answer instantly.
        if isOnlinePlayer[v.name] then
          v.lastseen = curTime
          --This replaces the usage of the variable 'found'.
          isOnlinePlayer[v.name] = nil
        end
      end
      for _, v in pairs(playerData.staff) do
        if isOnlinePlayer[v.name] then
          v.lastseen = curTime
          isOnlinePlayer[v.name] = nil
        end
      end
      for _, v in pairs(playerData.players) do
        if isOnlinePlayer[v.name] then
          v.lastseen = curTime
          isOnlinePlayer[v.name] = nil
        end
      end
      --All players that are still within isOnlinePlayer have to be added to the player list.
      for onlinePlayer in pairs(isOnlinePlayer) do
        local newPlayerData = {name = onlinePlayer, lastseen = curTime, firstseen = curTime}
        table.insert(playerData.players, newPlayerData)
      end
    end
    

    But again I'm not sure if your player list is really that big.

     

    Here are some other hints:

    1. You can use "xpcall(main, debug.traceback)" for more detailed information than "pcall(main)".
    2. It would also help if you add some kind of logging to your program. (open a file with append mode and write something to the file before and after critical/suspicious parts of your code. (don't forget to do file:flush() after file:write() to ensure that there is no delayed writing)
  14. I actually wrote a program to make looking at components and libraries easier:

    http://oc.cil.li/index.php?/topic/576-cbrowse-inspecting-lua-components-and-other-objects/

    You can navigate through everything using your mouse or the command line.

    You can go back via Ctrl+C.

    If you like to read a component method's documentation just input that:

    =tostring(method)

    To install the program you also need another program. (cbrowse is packed in a tar archive; this program creates and extracts them)

    http://oc.cil.li/index.php?/topic/421-tar-for-opencomputers/

     

    To make it a bit easier for you, here are the commands for installation (needs an internet card):

    cd /
    wget 'https://raw.githubusercontent.com/mpmxyz/ocprograms/master/home/bin/tar.lua'
    wget 'https://raw.githubusercontent.com/mpmxyz/ocprograms/master/tars/cbrowse.tar'
    tar -xf cbrowse.tar
    
  15. Update (07.11.15)

    The API has changed a bit.

    pid.remove now expects a controller instead of an id.

    Use pid.removeID for the old behaviour.

    The command line interface of pid has been changed, too.

    It now has the strict format of:

    pid <action> id or file [other stuff]
    

    (Note: actions are no longer written with "--" in front of them.)

    This change has been introduced to allow changing controller properties:

    pid run /pids/turbine.pid p=10 i=0.4 d=1 f=4 target=900 --args bada4648-3559-4784-b3c7-06c146d9dc3b
    

    There also is a new program:

    gpid
    

    It has no options or parameters but instead provides you a graphical user interface.

    Use your mouse or the arrow keys + enter to navigate.

    I'm going to write a more detailed description about this program.

    One thing that might need to be explained is that after pressing "Add New" you have to input the absolute path of the controller file and after that you have to write the arguments for the controller as lua code. Since this is part of a new feature you can leave it empty and just hit enter when you are testing the program with your own controllers.

    gpid.png

  16. You want to do it manually in the shell?

    /> mkdir myProgram        #create a directory
    /> cd myProgram           #move to the newly creaeted directory
    /myProgram> edit test.lua #edit a file in the directory
                              #Attention: /test.lua would refer to a file at the root directory Was that your error?
    

    Or did you try to write lua code? Then it would be good to see what you have to help debugging.

  17. Your example library translation looks fine except for

    thisTable.1 = ...
    

    which should be

    thisTable[1] = ...
    

    You can add a custom location to the package searching path. You just have to modify the variable package.path. (i.e. by appending ";/myProgram/?.lua")
    But since this is looking more like a simple configuration file using the function dofile is another option.

    package.path = package.path .. ";/myProgram/?.lua" --This is just a quick and dirty example. You should only append this string once - i.e. when the computer is starting - and not every time you start your program.
    local keeplistAPI = require("keeplistAPI")
    print(keeplistAPI[1].dmg)

    vs.

    local keeplistAPI = dofile("/myProgram/keeplistAPI.lua")
    print(keeplistAPI[1].dmg)
    

    There are a lot of other things that need some work.

    Like the fact that OC has got a different API for component interaction:

    local component = require("component")
    local sides = require("sides")
    local rs = component.redstone --alternatively you could use component.get("address-of-your-component")
    rs.setOutput(sides.left, 15)
    
  18. Is this function called within an event handling function or do you handle events "manually" in your main loop?

    os.exit() internally works by throwing an error. Due to that it isn't working within event listeners. (Listener errors are just written to a file and not forwarded to the main code.)

    If you want it to work correctly you have to have some kind of variable like "keepRunning" that finishes your main loop when changed.

    By the way: Don't forget to event.ignore before stopping your program! :)

  19. Interesting idea...

    To make writing programs for that kind of system easier it would be a good idea to use a special table with an index and a newindex metamethod.

    They would interact with other computers if necessary. (e.g. when fetching a block of data)

    Internally they could use a weak table to allow for non permanent caching. (Attention: you would have to wrap non table values in tables to make that work. Tables don't shrink after "loosing" values.)

    For high memory efficiency you can store your data in e.g. 1 KiB strings. That is quite easy to include in the previously mentioned metamethods and makes the communication between computers more efficient. (Lua strings are 8 bit clean; you can store a 32 bit integer using 4 characters without 0-issues.)

    The only problem is that you have to write your own bytes-to-number conversion function. (vanilla Lua 5.3 has that kind of functionality included; I'm not sure about OC Lua 5.3.)

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.