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

Fingercomp

Members
  • Content Count

    132
  • Joined

  • Last visited

  • Days Won

    34

Fingercomp last won the day on July 31

Fingercomp had the most liked content!

2 Followers

About Fingercomp

  • Rank
    Leading Member

Profile Information

  • Gender
    Male

Contact Methods

  • GitHub
    Fingercomp
  • IRC
    fingercomp

Recent Profile Visitors

2602 profile views
  1. Anything not impossible is possible, and this feature doesn't even require much perseverance to implement. Right now we're using owner to store the nickname of one player. We are going to replace it with a table, and therefore the name owner is no longer satisfactory. Let's rename it as trustedPeople and populate it with some test names. local trustedPeople = {"player1", "player2", "player3"} A table is the universal record type in Lua, that is, the type that stores multiple values. But we still need to choose the correct data structure. The table trustedPeople is a sequence. This means its elements are stored sequentially, without gaps, and can be accessed using integer indeces (trustedPeople[1], trustedPeople[2], etc.). Such a data structure is useful if we need an ordered collection of items to iterate over, for example. However, this is not the case here. What we need is essentially a set, which stores unique elements and can tell us in an efficient way if a certain value exists. In Lua, sets are usually implemented as follows: local trustedPeople = { ["player1"] = true, ["player2"] = true, ["player3"] = true, } The stuff in square brackets is called the key, and what follows after the equals sign is the value. If you have the key, you can quickly determine the associated value, which defaults to nil for nonexistent keys. The most important word is quickly. As for the previous data structure, to find if a table contains a trusted person, we would have to iterate over the sequence of authorized players for every element of the table. local ownerNearby = false for _, entry in ipairs(scan) do for _, trusted in ipairs(trustedPlayers) do if entry.name == trusted then ownerNearby = true goto out end end end ::out:: It's very inefficient and complicated. Now, compare it with the code that we would write if we use a set for trustedPlayers: local ownerNearby = false for _, entry in ipairs(scan) do if trustedPlayers[entry.name] then ownerNearby = true break end end However, I like how easy it is to modify the sequence in the code. {"one", "two", "three"} is much more concise than {["one"] = true, ["two"] = true, ["three"] = true}. I'll make it so that we could specify the list of allowed people, and it would be converted to a set by the program. local trustedPeople = {"player1", "player2", "player3"} for index, nickname in ipairs(trustedPeople) do trustedPeople[nickname] = true trustedPeople[index] = nil end Let's incorporate all of that in the original program. local component = require("component") local alarm = component.os_alarm local door = component.os_door local entity = component.os_entdetector local trustedPeople = {"player1", "player2", "player3"} for index, nickname in ipairs(trustedPeople) do trustedPeople[nickname] = true trustedPeople[index] = nil end local previousState = "nobody" while true do local scan = entity.scanPlayers(64) if scan and #scan > 0 then local shouldOpen = false for _, entry in ipairs(scan) do if trustedPeople[entry.name] then shouldOpen = true break end end if shouldOpen and previousState ~= "allowed" then door.open() alarm.deactivate() print("Welcome home!") previousState = "allowed" elseif not shouldOpen and previousState ~= "intruder" then door.close() alarm.activate() print("Hello, whoever you are.") previousState = "intruder" end else door.close() alarm.deactivate() previousState = "nobody" end os.sleep(2) end I've also changed a couple of names so that they would reflect their purpose.
  2. I am sorry, but unless I'm misreading your code, it can't possibly throw an "attempt to index a nil value" error, with any people in proximity or without. And the question is contradictory, stating both that the program crashes and that it freezes (the latter is correct, however). Actually, I'm unsure what the actual question is. Therefore, I'll do the only thing I can here: that is, explain what your code does wrong and how to fix it. Before I can proceed to fix the actual issues in the program's logic, I have to get rid of some minor problems. The first such problem is that the code is indented poorly. Of course, Lua doesn't care how you format your code, but it isn't why you do that. Indentation helps you, the program's author, to keep track of what it does, to visually separate chunks of code. And other people benefit from that too, for properly formatted code is much more readable. Without further ado... local component = require("component") local colors = require "colors" gpu = require("component").gpu alarm = require("component").os_alarm door = require("component").os_door entity = require("component").os_entdetector local w, h = gpu.getResolution() local delay -- sleep time local player -- list of players while true do os.sleep(2) if entity.scanPlayers(64) == true then scan = entity.scanPlayers(64) name = scan[1].name if name ~= "sav" then door.close() alarm.activate() print("Hello " .. scan[1].name) os.sleep(2) alarm.deactivate() elseif name == "sav" then door.open() alarm.deactivate() print("Welcome Home Sav") os.sleep(2) end os.sleep(4) end end ...And the second thing to take care of is the global variables. Unless you need them such (which rarely happens and this is no exception), make them local. Finally, you've already required component, so there is no reason to do so again; and, while fixing that, let's also remove the unused variables: gpu, colors, delay and player. You can re-add them later yourself if you need. local component = require("component") local alarm = component.os_alarm local door = component.os_door local entity = component.os_entdetector local owner = "sav" -- insert your nickname here while true do os.sleep(2) if entity.scanPlayers(64) == true then local scan = entity.scanPlayers(64) local name = scan[1].name if name ~= "sav" then door.close() alarm.activate() print("Hello " .. scan[1].name) os.sleep(2) alarm.deactivate() elseif name == "sav" then door.open() alarm.deactivate() print("Welcome Home Sav") os.sleep(2) end os.sleep(4) end end Great, so now we can start dissecting the code. What does it do? First, we require the component library, get proxies of the components we need, and proceed into the main loop. There we first sleep for 2 seconds... Why do we sleep for 2 seconds, again? I'm not sure; let's keep it there for now. Then we perform a scan, calling entity.scanPlayers(64), which returns a table, and compare if it is equal to true. Wait. There is no way a table can be equal to true, not unless you mess with metamethods, which we don't. Very suspicious. If we interpret the code literally, we skip the whole if, and end up at the start of the loop again. It's not interesting, so let's pretend that somehow the if's condition is satisfied to understand what the author's intent was. And then we perform another scan... hang a minute, another one? Haven't we already done that? Why should we be in dire need of repeating ourselves? Anyway, I think I realized what the code was supposed to do: scan the surroundings and check if there's anyone around. After all, running an alarm without anybody to listen to the marvelous sounds of it is just a waste of energy, isn't it? Let's put it into words in Lua. ... while true do local scan = entity.scanPlayers(64) if scan and #scan > 0 then ... else door.close() alarm.deactivate() end os.sleep(2) end We do the scan as soon as we're in the loop. If the scan result is present (neither a false nor a nil), which means the entity detector had no errors, and the length (#) of that table (#scan) is greater than 0 (#scan > 0), then we'll do something about it. The previous sentence is basically how you'd read the code in English. Note that if scan happens to be empty, we close the door and disable the alarm. And we shouldn't forget to sleep, for the sake of energy conservation. All right, so let's do something about the scan. scan the variable is a table of tables. In other words, scan is a table that stores other tables. Each "other table", in turn, contains information about one player nearby. We have already checked that at least one player is present, but there may be more people gathered around, totally not planning a siege, and we don't yet know whether you are there. You blindly assume you would be the first player in the returned list. Unfortunately, the entity detector couldn't care less about that, and your entry could easily be the second or the last one. Or not present at all, if you happened to carelessly abandon your fortress. Our mini-goal is to walk through all the entries of the table (this is called iterating a table) until your entry is found. If after iterating through all items we have yet to found yours, you're not around. So we close the door and activate the alarm. Otherwise, we do the opposite. This is how you do it: local ownerNearby = false for _, entry in ipairs(scan) do if entry.name == owner then ownerNearby = true break end end if ownerNearby then door.open() alarm.deactivate() print("Welcome home!") else door.close() alarm.activate() print("Hello, whoever you are.") end Something isn't right... The program will keep spamming "Welcome home!" if you're in vicinity, which isn't quite courteous. Let's make it greet you only if you weren't around before that, and do likewise for greeting intruders. To do that, we need to remember the previous state of your program: whether there was nobody, an intruder, or you. local component = require("component") local alarm = component.os_alarm local door = component.os_door local entity = component.os_entdetector local owner = "sav" -- insert your nickname here local previousState = "nobody" while true do local scan = entity.scanPlayers(64) if scan and #scan > 0 then local ownerNearby = false for _, entry in ipairs(scan) do if entry.name == owner then ownerNearby = true break end end if ownerNearby and previousState ~= "owner" then door.open() alarm.deactivate() print("Welcome home!") previousState = "owner" elseif not ownerNearby and previousState ~= "intruder" then door.close() alarm.activate() print("Hello, whoever you are.") previousState = "intruder" end else door.close() alarm.deactivate() previousState = "nobody" end os.sleep(2) end Well, this is the whole program. I didn't test it, so any bugs are yours to find and exterminate.
  3. It's hard to find the error because indentation is a mess, to be honest. I strongly recommend that you learn how to indent the code properly. If you do that, you should get something like https://pastebin.com/D5adZ0HV. Look at the line 97: you can easily notice that the position of elseif seems weird. Backtrack a little to find where it went wrong — and there, the line 44 contains an extraneous end. Now, if you remove it and re-indent the code again, you'll find out that the last line seems out of place. Sure enough, if you just remove that end, there will be no other end to close the if! So you should add end before that last line. After all of that, the original problem will be solved. I'll note that the code uses global variables throughout; it has sections like L59–87 which could be replaced by a for loop (and which could take 4 lines of code instead of 29); event.listen is called incorrectly (the anonymous function should be the second argument of the call but is instead discarded); the repeat loop with a dubious exit condition can be replaced with a more relatable while true do loop. All of those are either present problems or potential issues that can manifest themselves when you least expect them to, and worth fixing.
  4. There are three ways to turn on a computer that don't require player interaction: use some kind of an autoclicker to shift-click on the computer case; set a wake message and repeatedly send it over the network; configure a redstone card or a redstone I/O block to turn on the computer when either of them receives a redstone signal and connect a clock generator to it. I guess 3 is the best choice here (and it works for servers, too). And it effectively solves your problem, because computers won't start unless they are powered.
  5. Yes, I forgot to mention it, but the sender side was also a microcontroller. Note that I've tested this on the latest OC dev build for MC 1.12.2. But I could reproduce the bug on OC 1.7.5 (same MC version). So, perhaps it was already fixed. I suggest that you also try running the latest dev build: https://ci.cil.li/job/OpenComputers-MC1.12/ There's an issue on the bug tracker that described a very similar problem. If updating the mod version doesn't resolve the problem, you could post a comment there asking the issue to be reopened.
  6. I've just tried running exactly the same code you have, and it worked. Although the µC turned off right after that, as the its program doesn't have any kind of loop to keep the µC busy. This may be the reason you don't notice that it actually turns on.
  7. This commit (2017-03-30) added getDeuterium and getTritium: https://github.com/mekanism/Mekanism/commit/3eba40c8700a5165f3ce52088643f141c8d0ab57 Here's the PR: https://github.com/mekanism/Mekanism/pull/4282 Since you don't have these methods, you're probably using some really outdated version of Mekanism. You could update the mod or patch it yourself.
  8. If you're running OpenOS, there's a proper way to disable hard interrupts: require("process").info().data.signal = function() end. This function is the interrupt handler, and by populating the body of the function, you can define the desired behavior when Ctrl-Alt-C is pressed. Though the pcall solution makes sense if you want to restart the program when it crashes (because of a bug, for instance). For the sake of completeness, soft interrupts, Ctrl-C without the Alt key, generate an interrupted event.
  9. Let's solve the problem iteratively, one step at a time. The first thing to do is to define the task thoroughly. Our goal is to make a program that checks periodically if there's an authorized person, opens the door if there is one, and closes it otherwise. Then we should require everything we need — that is, the door and the RFID reader. local door = require("component").os_door local rfid = require("component").os_rfidreader This is almost the same as what you did in your code, but notice that the variables I've defined are local. You may wonder what it means and why I'm doing this. I'm not going to answer the former question here, lest the answer be too long to read without feeling bored. Instead, I'll answer the latter question. Making every variable local helps to prevent odd bugs with "ghost" variables (those that possess a value, albeit not being set anywhere in the program), complicating program development needlessly, and serves to urge the programmer to pass data between functions explicitly instead of relying on global variables. Though, if I am to be honest, it won't make a difference in this program. Let's move on. The program needs to know the nick of the authorized user. local authorized = "johnsmith" Now we'll need the program to repeat some actions (checking surroundings, managing the door) indefinitely. For that we use a loop — specifically, an infinite loop. while true do ... end The ellipsis is a placeholder for the loop body, which will be repeated. Let's replace it with actual code. First, perform a scan. local scan = rfid.scan() scan is now a table of tables. Each subtable contains information about one player. Because we don't know which subtable corresponds to the authorized person, we have to check every entry of scan. We'll use a for loop. local shouldOpen = false for _, player in ipairs(scan) do ... end player is a table. The nick of each player is stored in the field name. We can access its value with player.name. If it's equal to authorized, we'll set the shouldOpen variable to true. Otherwise, we look further. If that person is not detected by the scanner, shouldOpen will remain false. for _, player in ipairs(scan) do if player.name == authorized then shouldOpen = true break end end If we find the user, the break statement stops the for loop prematurely. Also note that we don't need to put local before shouldOpen = true: local defines a new variable, whereas we just want to reassign an existing one. Now that we know if there's a trusted user nearby, we can control the door. if shouldOpen then door.open() else door.close() end If the door is already open, door.open() does nothing (likewise if we .close() a closed door), which is what we want. Finally, we add an os.sleep to wait a second before checking again. os.sleep(1) Gathering the snippets together, we get the finished program. local door = require("component").os_door local rfid = require("component").os_rfidreader local authorized = "johnsmith" while true do local scan = rfid.scan() local shouldOpen = false for _, player in ipairs(scan) do if player.name == authorized then shouldOpen = true break end end if shouldOpen then door.open() else door.close() end os.sleep(1) end
  10. I had this problem quite a long time ago, and I think I fixed it by also updating the settings client-side to match those on the server.
  11. There was no IRC library for OpenComputers, so I've made one. Here's a demo bot that uses it: local com = require("component") local event = require("event") local thread = require("thread") local gpu = com.gpu local irc = require("irc") local events = irc.events local env = setmetatable({ irc = irc, events = events, }, {__index = _G}) local client = irc.builder() :connection { host = "irc.esper.net:6667", throttling = { maxDelay = 2, maxThroughput = 5, }, } :auth { nickname = "oc-finger-irc", username = "fingercomp", realname = "OpenComputers IRC client library", } :bot { channels = {"#oc-finger-irc"}, tracking = { users = true, modes = true, account = true, userInfo = true, }, } :execution { threaded = true, reconnect = true, catchErrors = true, } :subscribe(events.irc.command, events.priority.high, function(self, client, evt) gpu.setForeground(0x00ff00) print("→ " .. evt.rawLine) gpu.setForeground(0xffffff) end) :subscribe(events.irc.write, events.priority.normal, function(self, client, evt) gpu.setForeground(0x00dbff) print("← " .. evt.line:gsub("[\r\n]*$", "")) gpu.setForeground(0xffffff) end) :subscribe(events.irc.message, irc.events.priority.normal, function(self, client, evt) if evt.source.nickname == "fingercomp" then if evt.message == "::quit" then evt:reply("Quitting.") evt.client:stop(("%s told me to quit."):format(evt.source.nickname)) elseif evt.message == "::spam" then evt:reply("1") evt:reply("2") evt:reply("3") evt:reply("4") evt:reply("5") elseif evt.message == "::longmsg" then local msg = {} for i = 1, 256 do if i == 128 then table.insert(msg, tostring(i) .. " ") else table.insert(msg, tostring(i)) end end evt:reply(table.concat(msg)) elseif evt.message == "::error" then (nil).test() elseif evt.message:sub(1, #"::exec ") == "::exec " then local code = evt.message:sub(#"::exec " + 1) local chunk, reason = load("return " .. code, "=irc", "t", env) if not chunk then chunk, reason = load(code, "=irc", "t", env) end if not chunk then evt:reply(("\x0304Error:\x0f %s"):format(reason)) else local result = table.pack(xpcall(chunk, debug.traceback)) local success = table.remove(result, 1) result.n = result.n - 1 for i = 1, result.n, 1 do if type(result) ~= "string" and type(result) ~= "number" and type(result) ~= "boolean" and type(result) ~= "nil" then result[i] = tostring(result[i]) else result[i] = ("%q"):format(result[i]):gsub("\\\n", "\n") end end if not success then evt:reply(("\x0304Error:\x0f %s"):format(result[1]:match("^[^\n]*"))) io.stderr:write(("%s\r\n"):format(result[1])) elseif result.n > 0 then evt:reply(table.concat(result, " ", i, result.n)) else evt:reply("\x0309Success") end end end end end) :subscribe(events.irc.ctcpRequest, irc.events.priority.normal, function(self, client, evt) if evt.ctcpCommand == "TIME" then evt:reply(os.date("%F %T")) end end) :subscribe(events.client.error, irc.events.priority.top, function(self, client, evt) print("Caught: " .. evt.traceback) evt:cancel() end) :build() local t = thread.create(function() repeat local evt = event.pull("interrupted") until evt end) env.client = client client:run() thread.waitForAny({t, client.thread}) client:stop("Quitting.") os.exit() I don't want to brag about it too much, but I think the code is pretty. Features Threaded execution. A nice API. IRCv3.2 capability negotiation. Throttling to prevent the bot from triggering anti-flood mechanisms. Tracks channel members, modes, nick changes, account names, etc. Automatically reconnects if it's disconnected from the IRC server. Splits long messages over multiple lines. The event bus can use coroutines as message handlers. Links The GitLab repository: https://gitlab.com/cc-ru/irc-oc Documentation: https://gitlab.com/cc-ru/irc-oc/-/wikis/home Demo programs: https://gitlab.com/cc-ru/irc-oc/snippets
  12. TL;DR: set the resolution to 159×28. Displays automatically scale the content to fit the screen's inner area. If you decrease the resolution height, the display area will occupy more horizontal space. I'll assume that your screen is 8×3. The aspect ratio of the screen's inner area is (2 × (8×16 - 4.5)) : (3×16 - 4.5) = 494×87. The "- 4.5" terms are the screen borders, and the width is doubled because the cell width is half its height. If you set the resolution proportional to this ratio, it will fill the whole screen. Of course, you can't do this, as the maximum resolution is 160×50. So we have to compromise and choose the resolution that fits the screen the best. local function round(num) local integer, frac = math.modf(num) if frac >= 0.5 then integer = integer + 1 end return integer end local data = {} for w = 1, 160, 1 do local h = math.max(math.min(round(w * 87 / 494), 160 * 50 / w, 160), 1) local area = w * h local delta = (w / h) - 494 / 87 table.insert(data, {delta, area, ("%d×%d"):format(w, h)}) end table.sort(data, function(lhs, rhs) return lhs[1] < rhs[1] end) local f = io.open("./data", "w") for k, v in ipairs(data) do f:write(("%.6f %d %s\n"):format(v[1], v[2], v[3])) end f:close() The above code creates an array and fills it with 160 possible resolutions by choosing a width and calculating the height such that the aspect ratio is the closest to what we're trying to achieve (494:87). Then it writes the data to a file so that we can plot it. Here's what we get: Now it's clear that the best resolution is 159×28. Its aspect ratio differs from 494/87 by ~0.000411, which is really small. Let's go ahead and calculate the thickness of the black area. Let dw, dh be the dimensions of the display area, and iw, ih the dimensions of the inner area. dw/dh = 159/28; iw/ih = 494/87. 159/28 > 494/87, so the display area height is less than the screen's inner area height. Therefore, the width of the black area is dw = iw. The height equals 0.5 × (ih - dh) = 0.5 × (87/494 × iw - 28/159 × dw) = 0.5 × iw × (87/494 - 28/159) = 0.5 × 494/87 × ih × (87/494 - 28/159) = 1/27666 × ih. In other words, the height of the black area is 1/27666th of the whole inner area height, which is negligible.
  13. Pressing the button that is placed on the screen turns the screen on/off. Make sure the button is not adjacent to the screen. Also, you should use rs.getInput in yor problem. rs.getOutput returns the strength of the redstone signal emitted by the computer's redstone card.
  14. Fingercomp

    Drone usage?

    Of course there is. Assembling a drone with a leash upgrade produces a drone that can transport cows, horses, sheeps, and pigs. Like this: Or... like this: Besides, drones are entities. They move in straight lines; they don't get stuck in the fence and can actually enter a house through a door. They move pretty fast — about 6 blocks a second (robots are at least 2 times slower). Drone's movement is asynchronous, so the machine can execute some other code in the middle of the flight. Drones can fly above the block height limit. The screenshot above was made while enjoying the flight at a height of 260 blocks, for example. Oh, they can fly below the bedrock, too. Also, drones look cool. Jobs at which drones clearly outperform robots include player transportation (thanks to the leash upgrade) and item transportation (due to their mobility). And, if used with robots, drones can be useful for crop farming, autocrafting, and construction, for example.
  15. It's a bit unclear for me what kind of problem you have. Is print also called twice per supposedly one sent modem message? Or is it only the doHarvestRoutine call that's repeated? If it's the former case (the print line is also run twice), it means your sent messages are somehow duplicated. First thing to check for is whether you have any relays nearby. These blocks LOVE to mess up with network packets. If you use them, I advice you to remove them and use linked cards or a network that checks for duplicates. If you don't, it's very likely that the sender program has a bug that causes it to send each packet twice.
×
×
  • Create New...

Important Information

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