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


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by Fingercomp

  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.
  16. The last chapter mentions briefly the biggest problems that make sound reconstructed from the audio data sound bad, and I'm going to try to explain them. 1. The program is quite simple. It's merely a proof of concept. It doesn't use modulation or non-sine waveforms. 2. Most sounds are complex. Most sounds I hear are far from being purely sinusoidal. Perhaps it's different for you, or for someone else who happens to read this post. :) White noise is vastly different from a flute, and the flute isn't the same as a guitar. Each instrument has a unique timbre, and even a single instrument can have different timbres depending how performers play it. Sound can be decomposed into an infinite number of sinusoids that, when summed together, produce the original sound. This is what Fourier transform does, basically. So if you use it to decompose a sound of a violin playing, and filter the output, you'll see, indeed, multiple sine waves that construct the signal, which are called partials. When you reconstruct the sound back, to get better approximation, you need to sum more partials. 3. Unfortunately, the sound card only has 8 channels (by default). This means that from the set of sinusoids that represent the encoded signal we have to choose eight of them. It's enough for a piano, but for some sounds (such as human speech) it's clearly too few of them, and what you get doesn't even seem to be similar to what was originally encoded. It's worth noting that if the program also analyzed the spectrum to use different waveforms, it could probably achieve a better resemblance. 4. Furthermore, we discard phase. The Fourier transform produces two values for each sinusoid: the amplitude, and the phase. The phase is important. For example, if you have two sine waves that differ in phase by π, and have the same frequency and amplitude, you won't hear any sound. These sinusoids are opposite of each other: if, at some point, one has a value of 0.5, the other has the value of -0.5, so, when you sum them, you get zero everywhere. The sound card has no way of setting phase. It's possible to get the situation as described above: if the first channel accumulates phase of π, and then you initialize the second channel, you may hear no sound at all! Though, given the limited channel count, the fact that we discard the phase doesn't really make much difference. But the channel count can be changed in the configuration file. When I was testing the program, I had tried running it with 64 channels. It was quite noisy, because the program does not filter the output of Fourier transform, and doesn't set phase. It was also glitchy, caused by my computer, which was unable to process everything fast enough. But it was still better than 8 channels.
  17. A byte is an octet of bits, which can store 2⁸ = 256 possible values. To represent a character as a number, we need an encoding system. ASCII is one of them, and it assigns first 128 values to control characters, digits, English letters and punctuation. But people also use different languages, which need additional characters to be represented. Latin-1, for example, uses the remaining 128 values to define a few more characters from Latin-based scripts. Even with that, the character coverage was low: Chinese has several thousand different characters, for instance. Therefore, that was a need for an encoding system that allows to represent, ideally, characters from all scripts in use. Unicode is such a standard, and its latest revision has more than 130 thousand characters mapped to codepoints. Unicode has several possible encodings, e.g. UTF-8, UTF-16, and UTF-32, also defined by the Unicode standard. UTF-16 uses 16 bits (2 bytes) for each character, and UTF-32 uses 4 bytes. But they occupy too much data storage. UTF-8 has gained more use, at least on the web, and it uses 1 to 4 bytes to represent each character (so it usually uses less space). It's also the encoding used by OpenComputers. This is how UTF-8 encodes the string "¯\_(ツ)_/¯": Byte: c2 af 5c 5f 28 e3 83 84 29 5f 2f c2 af Chars: \___/ \/ \/ \/ \______/ \/ \/ \/ \___/ ¯ \ _ ( ツ ) _ / ¯ As you can see, some characters are encoded with a single byte (such as _, (, ), /, \); some need 2 bytes (¯); the tsu character in the middle (ツ) needs 3 bytes. When you open a file, there's a question of what it should work with. There are two possible answers. Non-binary. The stream works with UTF-8-encoded characters. Assuming the stream is at the beginning of the string "¯\_(ツ)_/¯", file:read(2) will return "¯\" — that's 3 bytes (#"¯\" == 3). This is the default mode. Binary. The stream works with single bytes. With the same assumption, file:read(2) will return "¯", a single character. If you need to use it, you have to explicitly set it by adding the character "b" to the mode string (io.open(path, "rb"), for instance). In binary mode, CC is unable to store non-UTF-8-encoded data in its strings (in other words, it does support strings encoded with UTF-8, but not raw bytes), so it has to return a number instead. This is not how PUC-Rio's implementation of Lua, or OC's natives (which are based on the former) work — they return a string with a single byte instead. But you can get the byte number by using the function string.byte, and string.char to do the opposite. local str = "\xc2" local byte = string.byte(str) print(byte == 0xc2) --> true print(string.char(byte) == str) --> true
  18. This behaviour is strange and indicates there's a bug somewhere. Since the double buffering library is used by MineOS, it would have much more chance of being noticed, so it's more likely there's some problem in your code. Could you show the code you run, please?
  19. Yeah, basically. The buffer is a singleton. In other words, there's only a single buffer instance in the memory, which is kept there even if programs that required the library exited. An implication of this fact is that buffer may be non-empty when you start your program; therefore, before you use the buffer, you need to do a buffer.clear() followed by a forceful render: buffer.drawChanges(true). It's been a long time since I've used that library, though. Things might have changed. Considering there aren't proper releases and commit descriptions, it's rather difficult to navigate through the commit history.
  20. OC sets significant limits the GPU, which make it much harder to do more advanced graphics. You can optimize the calls at the cost of high RAM usage — storing data in a buffer and only drawing the cells that changed since the last buffer flush. It isn't really an easy thing to implement yourself, so you might want to use a buffering library someone else made — here's one that definitely works, for example. I was also making one, but it's not yet ready. OpenComputers has a font.hex file, which is the font that's used to render characters on the screen. Since OC 1.6, asie's funscii font is used. Let's make things clear. Unicode is a standard that assigns numbers (codepoints) to a ton of characters (glyphs from different scripts, math symbols, emoji, and some other random things). UTF-8 is one of the possible encodings for Unicode symbols, and it's fairly the most popular one used. It converts a character codepoint, like U+2588, to a sequence of bytes. We need an encoding because a byte can only contain 256 values, while Unicode has 137439 characters. OpenComputers (and a lot of other software) supports UTF-8 and uses it to encode characters. OpenComputers provides the Unicode API for Lua 5.2 and Lua 5.3 architectures. You can use it to convert codepoints to characters and perform other operations on UTF-8 strings. Lua 5.3 also exports the utf8 library. It has a few functions that Unicode API doesn't provide, but is unavailable for Lua 5.2.
  21. Yeah, there is a way to make it faster. In fact, way faster. One of the things that makes it slow is... gpu.setBackground. It's quite expensive in terms of perfomance (tier 3 GPUs consume 1/128 of the call budget when this method is called). Fortunately, your program only involves 4 colors, so it can be easily optimized. Actually, there are two ways to do it. A somewhat naive way would be to process all cells of one color first, then process all cells of another color, etc. That would solve the issue with gpu.setBackground, but there's another thing to worry about. The gpu.set method isn't free. In fact, that program does 160 × 50 = 8000 sets, assuming it's running on the max tier 3 resolution. And that is a lot. Fortunately, OC supports Unicode. In particular, there's a character in Unicode called a full block — █. On OC, I prefer to think of it as of an inverse of " " (a space). If the foreground and background colors are swapped, the space would look like the full block, and vice versa. And it can help us reduce amount of GPU calls significantly. Try replacing the loops with the following: -- i = 0 gpu.setBackground(0xff0000) gpu.set(1, 1, " ") -- i = 1 gpu.setBackground(0x00ff00) gpu.set(1, 2, " ") -- i = 2, 3 gpu.setForeground(0xffff00) gpu.setBackground(0x0000ff) local fullBlock = "\u{2588}" local oddRow = (" " .. fullBlock):rep(math.floor(mx / 2)) local evenRow = (fullBlock .. " "):rep(math.floor(mx / 2)) for y = 1, my do local x = 1 if y % 2 == 1 then if y == 1 then -- Shift the x by 2 to the right to avoid rewriting -- the first two characters. -- OC truncates everything that goes out of the screen bounds, -- so there's no need to worry about that. x = x + 2 end gpu.set(x, y, oddRow) else gpu.set(x, y, evenRow) end end I can't launch Minecraft to run the program and measure the render time, but I can calculate the amount of call budget consumed by the GPU. Assuming the program runs on a tier 3 GPU and screen, there are 4 gpu.setForeground/gpu.setBackground calls, and 52 gpu.set calls. We get (4 × 1 / 128) + (52 × 1 / 256) = 15 / 64 ≈ 0.23. This is enough even if you have a tier 1 processor (whose call budget is 0.5), and the program should be able to run at 20 FPS, though it doesn't really change each frame. Oh, I meant to say "T3" (tier 3), sorry. A tier 2 GPU can only perform 128 sets and 64 fills. https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala#L55-L60 Also, there's a comment a few lines above in that file, which reminds me of yet another reason OC makes indirect calls take 1 tick to run — synchronization. OpenComputers, very much unlike ComputerCraft, saves the state of running computers so that they can be resumed when the chunk is reloaded. Methods that interact with the world in some way (like moving items around) must perform synchronization with the Minecraft world to avoid, well, messing things up. The comment says it's especially important on world saves. Despite that, the GPU methods were made direct, although a "nasty trick" was needed to do this. Well, safe concurrency is hard to achieve.
  22. Please calm down. There are some component methods that block the computer for a tick when called, yes. Such methods usually interact with the world. For example, a transposer allows to transfer items between invetories by calling the transferItem method, which takes a tick to execute (so you get up to 20 stacks/second). On the other hand, there are a lot of direct methods, which can be called several times per tick. For example, the GPU's methods are direct. And the processor (as well as RAM) determines how many direct calls are allowed per tick. I think T2 T3 setup allows you to call gpu.get one thousand times every tick. Or 256 gpu.sets. Or 128 gpu.fills. I also believe you don't realize how bad ComputerCraft is for servers. A really simple and stupidly-written program can eat up all the memory the server has. That's why OC limits the memory available for an in-game computer. And running CPU-intensive tasks on a CC's computer makes the whole server run slow, whereas OC requires scripts to pause to allow other in-game computer to run. OpenComputers imposes the limits not because the mod developers want users to suffer (although I do sometimes think of that when I try to do some stupidly complex things with it), but to make it server-friendly. What's important is that OC allows you to configure these limits for your needs. Have you opened its configuration file? It is more than a 1.5k lines long, and has more than 300 settings you can change, each with a comment that explains the effect of the setting. There's callBudgets to increase the direct call number limit. Or decrease it. There's also the ramSizes setting for memory, and hddSizes for the HDD sizes. But even with all of these limits, OC can run a lot of programs, including graphical shells, 3D games, reactor control systems. It allows you to send HTTP requests to the real websites, and open raw TCP sockets to interact with network services. It can display 320x200 images. It can even play videos — inside the game, and on the default OC settings!
  23. Oh, right... I forgot about that, sorry. Replace socket:close() with response:close().
  24. The code below tries to make sure that the request finished, and then it reads the response. Unfortunately, I can't test the program in the game for various reasons. Could you try using it? local c = require("component") local computer = require("computer") local internet = require("internet") local f = io.open("test.txt", "wb") local imax = 0 local xmax = 75 local ymax = 180 local zmax = 87 local TIMEOUT = 5 -- in seconds local link = "https://raw.githubusercontent.com/LordNocturnus/sf-" local folder = "/master/" local pos = 0 for i = 0, imax do for xb = pos, xmax do for yb = 0, ymax do for zb = 0, zmax do print(xb, yb, zb) local file = xb .. "-" .. yb .. "-" .. zb .. ".mb3d" local url = link .. i .. folder .. file local status, response = pcall(internet.request, url) if not status or not response then print("Download of " .. url .. " failed.") else local startTime = computer.uptime() while true do local result, isFinished = pcall(response.finishConnect) if result and isFinished then break elseif not result then print("Could not connect to " .. url .. ".") os.exit() elseif computer.uptime() - startTime > TIMEOUT then print("Request to " .. url .. " timed out") os.exit() else os.sleep(0.25) end end local f = io.open(file, "wb") for chunk in response do f:write(chunk) end f:close() socket:close() end end end end end
  25. Pay attention to what the installer says. It's where to install to, not what to install. You've been trying to install OPPM to a floppy disk or something, but you should've chosen the OpenOS filesystem instead to install it on the system HDD.
  • Create New...

Important Information

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