From b8bd64913bc0ca36d94e8388ac1e0b9fefac6a00 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:02:56 -0800 Subject: [PATCH 01/36] Add date-specific MOTDs (like Minecraft) (CCT#533) --- .../computercraft/lua/rom/programs/motd.lua | 29 +++++++++------ .../test-rom/spec/programs/motd_spec.lua | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/test-rom/spec/programs/motd_spec.lua diff --git a/src/main/resources/data/computercraft/lua/rom/programs/motd.lua b/src/main/resources/data/computercraft/lua/rom/programs/motd.lua index c8c75a40b..57ab7b916 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/motd.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/motd.lua @@ -1,15 +1,24 @@ -local tMotd = {} +local date = os.date("*t") +if date.month == 1 and date.day == 1 then + print("Happy new year!") +elseif date.month == 12 and date.day == 24 then + print("Merry X-mas!") +elseif date.month == 10 and date.day == 31 then + print("OOoooOOOoooo! Spooky!") +else + local tMotd = {} -for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do - if fs.exists(sPath) then - for sLine in io.lines(sPath) do - table.insert(tMotd, sLine) + for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do + if fs.exists(sPath) then + for sLine in io.lines(sPath) do + table.insert(tMotd, sLine) + end end end -end -if #tMotd == 0 then - print("missingno") -else - print(tMotd[math.random(1, #tMotd)]) + if #tMotd == 0 then + print("missingno") + else + print(tMotd[math.random(1, #tMotd)]) + end end diff --git a/src/test/resources/test-rom/spec/programs/motd_spec.lua b/src/test/resources/test-rom/spec/programs/motd_spec.lua new file mode 100644 index 000000000..d426fb6e7 --- /dev/null +++ b/src/test/resources/test-rom/spec/programs/motd_spec.lua @@ -0,0 +1,36 @@ +local capture = require "test_helpers".capture_program + +describe("The motd program", function() + local function setup_date(month, day) + stub(os, "date", function() return { month = month, day = day } end) + end + + it("displays MOTD", function() + setup_date(0, 0) + local file = fs.open("/motd_check.txt", "w") + file.write("Hello World!") + file.close() + settings.set("motd.path", "/motd_check.txt") + + expect(capture(stub, "motd")) + :matches { ok = true, output = "Hello World!\n", error = "" } + end) + + it("displays date-specific MOTD (1/1)", function() + setup_date(1, 1) + expect(capture(stub, "motd")) + :matches { ok = true, output = "Happy new year!\n", error = "" } + end) + + it("displays date-specific MOTD (10/31)", function() + setup_date(10, 31) + expect(capture(stub, "motd")) + :matches { ok = true, output = "OOoooOOOoooo! Spooky!\n", error = "" } + end) + + it("displays date-specific MOTD (12/24)", function() + setup_date(12, 24) + expect(capture(stub, "motd")) + :matches { ok = true, output = "Merry X-mas!\n", error = "" } + end) +end) From 8885462175bd092355bbd0e0e06c8515a953dbfd Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:05:22 -0800 Subject: [PATCH 02/36] Don't use entity.captureDrops at all (removed line) --- .../java/dan200/computercraft/shared/util/DropConsumer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java index ffca60fc3..c58a5727b 100644 --- a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java +++ b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java @@ -35,8 +35,6 @@ public final class DropConsumer { dropWorld = new WeakReference<>(entity.world); dropPos = null; dropBounds = new Box(entity.getBlockPos()).expand(2, 2, 2); - - // entity.getScale().captureDrops( new ArrayList<>() ); } public static void set(World world, BlockPos pos, Function consumer) { From 452464aa01318fa012488d19dc916144502f610b Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:11:29 -0800 Subject: [PATCH 03/36] add changelog + vienamese support --- .gitignore | 1 + README.md | 11 +++++++---- patchwork.md | 15 +++++++++++++++ .../resources/assets/computercraft/lang/vi.json | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 patchwork.md create mode 100644 src/main/resources/assets/computercraft/lang/vi.json diff --git a/.gitignore b/.gitignore index 0b28ad333..02be23186 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ .idea .gradle *.DS_Store +.project \ No newline at end of file diff --git a/README.md b/README.md index 65b2c3ce2..3d75b4892 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# CC:T for Fabric -# This is a 'complete' port -*complete as in it runs and works-ish* -I'm busy with other projects and so if anyone else wants to maintain this be my guest +# CC:Restitched Patchwork +# This is a Work In Progress Port +*it runs and works-ish* + +THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T + +The changelog is located at [PatchWork.md](patchwork.md) \ No newline at end of file diff --git a/patchwork.md b/patchwork.md new file mode 100644 index 000000000..7309efff7 --- /dev/null +++ b/patchwork.md @@ -0,0 +1,15 @@ +# Just my list of things I have ported over + +# Example +`commit` // Shows commit from CC:T + +Title // Commit Title + +SubScript // Desc of commit + +```md +5155e18de279a193c558aa029963486fd1294769 +Added translation for Vietnamese +Co-authored-by: Boom +``` + diff --git a/src/main/resources/assets/computercraft/lang/vi.json b/src/main/resources/assets/computercraft/lang/vi.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/vi.json @@ -0,0 +1 @@ +{} \ No newline at end of file From e9c11ff325c5372a95e58ede18f5c887210b7f8b Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:13:56 -0800 Subject: [PATCH 04/36] Translations for Vienamese --- patchwork.md | 18 ++++--- .../assets/computercraft/lang/vi.json | 49 ++++++++++++++++++- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/patchwork.md b/patchwork.md index 7309efff7..f47601f10 100644 --- a/patchwork.md +++ b/patchwork.md @@ -1,15 +1,21 @@ # Just my list of things I have ported over -# Example -`commit` // Shows commit from CC:T - -Title // Commit Title - +Format for the changelog of ported stuff +``` +commit // Shows commit from CC:T +commit2 // Shows a commit that is the same thing, just a clean up, only if right after +Title // Commit Title SubScript // Desc of commit - +``` ```md 5155e18de279a193c558aa029963486fd1294769 Added translation for Vietnamese Co-authored-by: Boom ``` +``` +7e121ff72f2b1504cd6af47b57500876682bac45 +ae6124d1f477487abab1858abde8c4ec49dfee3c +Translations for Vienamese +Co-authored-by: Boom +``` \ No newline at end of file diff --git a/src/main/resources/assets/computercraft/lang/vi.json b/src/main/resources/assets/computercraft/lang/vi.json index 9e26dfeeb..b4261b8cb 100644 --- a/src/main/resources/assets/computercraft/lang/vi.json +++ b/src/main/resources/assets/computercraft/lang/vi.json @@ -1 +1,48 @@ -{} \ No newline at end of file +{ + "gui.computercraft.tooltip.disk_id": "ID của đĩa: %s", + "upgrade.computercraft.speaker.adjective": "Ồn ào", + "upgrade.computercraft.wireless_modem_advanced.adjective": "Ender", + "upgrade.computercraft.wireless_modem_normal.adjective": "Không dây", + "upgrade.minecraft.crafting_table.adjective": "Chế tạo", + "upgrade.minecraft.diamond_hoe.adjective": "Trồng trọt", + "upgrade.minecraft.diamond_axe.adjective": "Đốn", + "upgrade.minecraft.diamond_pickaxe.adjective": "Khai thác", + "upgrade.minecraft.diamond_shovel.adjective": "Đào", + "item.computercraft.pocket_computer_advanced.upgraded": "Máy tính bỏ túi tiên tiến %s", + "item.computercraft.pocket_computer_advanced": "Máy tính bỏ túi tiên tiến", + "item.computercraft.pocket_computer_normal.upgraded": "Máy tính bỏ túi %s", + "item.computercraft.pocket_computer_normal": "Máy tính bỏ túi", + "item.computercraft.printed_book": "Sách in", + "item.computercraft.printed_page": "Trang in", + "item.computercraft.treasure_disk": "Đĩa mềm", + "item.computercraft.disk": "Đĩa mềm", + "block.computercraft.turtle_advanced.upgraded_twice": "Rùa tiên tiến %s %s", + "block.computercraft.turtle_advanced.upgraded": "Rùa tiên tiến %s", + "block.computercraft.turtle_advanced": "Rùa tiên tiến", + "block.computercraft.turtle_normal.upgraded_twice": "Rùa %s %s", + "block.computercraft.turtle_normal.upgraded": "Rùa %s", + "block.computercraft.turtle_normal": "Rùa", + "block.computercraft.wired_modem_full": "Modem có dây", + "block.computercraft.cable": "Dây cáp mạng", + "block.computercraft.wired_modem": "Modem có dây", + "block.computercraft.wireless_modem_advanced": "Modem Ender", + "block.computercraft.wireless_modem_normal": "Modem không dây", + "block.computercraft.monitor_advanced": "Màn hình tiên tiếng", + "block.computercraft.monitor_normal": "Màn hình", + "block.computercraft.speaker": "Loa", + "block.computercraft.printer": "Máy in", + "block.computercraft.disk_drive": "Ỗ đĩa", + "block.computercraft.computer_command": "Máy tính điều khiển", + "block.computercraft.computer_normal": "Máy tính", + "itemGroup.computercraft": "ComputerCraft", + "block.computercraft.computer_advanced": "Máy tính tiên tiến", + "tracking_field.computercraft.websocket_incoming.name": "Websocket đến", + "tracking_field.computercraft.websocket_outgoing.name": "Websocket đi", + "gui.computercraft.tooltip.computer_id": "ID của máy tính: %s", + "tracking_field.computercraft.coroutines_dead.name": "Coroutine bỏ đi", + "tracking_field.computercraft.coroutines_created.name": "Coroutine đã tạo", + "tracking_field.computercraft.http_download.name": "HTTP tải xuống", + "tracking_field.computercraft.http_upload.name": "HTTP tải lên", + "tracking_field.computercraft.http.name": "Yêu cầu HTTP", + "gui.computercraft.tooltip.copy": "Sao chép vào clipboard" +} From 63cd9c5bc7ef9f7f8cfc2546f8a9f18625886c2e Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:20:51 -0800 Subject: [PATCH 05/36] Update Patchwork.md with format --- patchwork.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/patchwork.md b/patchwork.md index f47601f10..4a19c4040 100644 --- a/patchwork.md +++ b/patchwork.md @@ -7,6 +7,9 @@ commit2 // Shows a commit that is the same thing, just a clean up, only if rig Title // Commit Title SubScript // Desc of commit ``` + +If a edit that is present in CC:T is not needed, i will skip over it + ```md 5155e18de279a193c558aa029963486fd1294769 Added translation for Vietnamese From 4643641d51a3603547a23f28aa2d89618b50b651 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:23:39 -0800 Subject: [PATCH 06/36] Update Patchwork, Handle Tabs when Parsing Json --- patchwork.md | 9 ++++++++- .../data/computercraft/lua/rom/apis/textutils.lua | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/patchwork.md b/patchwork.md index 4a19c4040..52b637d94 100644 --- a/patchwork.md +++ b/patchwork.md @@ -8,7 +8,8 @@ Title // Commit Title SubScript // Desc of commit ``` -If a edit that is present in CC:T is not needed, i will skip over it +If a edit that is present in CC:T is not needed, I will skip over it. +Any and all references to an issue number, are to be found on CC:T's repo. not this oen ```md 5155e18de279a193c558aa029963486fd1294769 @@ -21,4 +22,10 @@ Co-authored-by: Boom ae6124d1f477487abab1858abde8c4ec49dfee3c Translations for Vienamese Co-authored-by: Boom +``` + +``` +59de21eae29849988e77fad6bc335f5ce78dfec7 +Handle tabs when parsing JSON +Fixes #539 ``` \ No newline at end of file diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua index 40de6924b..576c6755e 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua @@ -432,7 +432,7 @@ do --- Skip any whitespace local function skip(str, pos) - local _, last = find(str, "^[ \n\r\v]+", pos) + local _, last = find(str, "^[ \n\r\t]+", pos) if last then return last + 1 else return pos end end @@ -472,7 +472,7 @@ do buf[n], n, pos = utf8.char(tonumber(num_str, 16)), n + 1, pos + 6 else local unesc = escapes[c] - if not unesc then error_at(pos + 1, "Unknown escape character %q.", unesc) end + if not unesc then error_at(pos + 1, "Unknown escape character %q.", c) end buf[n], n, pos = unesc, n + 1, pos + 2 end elseif c >= '\x20' then From 19273b369655aaca6997275c8b91504dd2963e1c Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:35:36 -0800 Subject: [PATCH 07/36] CC:R 1.92.0 --- .vscode/settings.json | 3 +++ README.md | 2 ++ gradle.properties | 4 ++-- patchwork.md | 17 +++++++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..de2a843a9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/README.md b/README.md index 3d75b4892..7d9aeb2aa 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # This is a Work In Progress Port *it runs and works-ish* +## Reached Parity with CC:T 1.92.0 + THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T The changelog is located at [PatchWork.md](patchwork.md) \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d728c1e8f..6e8772510 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,10 +2,10 @@ org.gradle.jvmargs=-Xmx1G # Mod properties -mod_version=1.91.2 +mod_version=1.92.0 # Minecraft properties -mc_version=1.16.2 +mc_version=1.16.3 mappings_version=31 # Dependencies diff --git a/patchwork.md b/patchwork.md index 52b637d94..44c0762f2 100644 --- a/patchwork.md +++ b/patchwork.md @@ -28,4 +28,21 @@ Co-authored-by: Boom 59de21eae29849988e77fad6bc335f5ce78dfec7 Handle tabs when parsing JSON Fixes #539 +``` + +``` +748ebbe66bf0a4239bde34f557e4b4b75d61d990 +Bump to 1.92.0 +A tiny release, but there's new features so it's technically a minor +bump. +``` + +Cherry Picked because this update was partially related to forge updates rather than mod updates +``` +8b4a01df27ff7f6fa9ffd9c2188c6e3166edd515 +Update to Minecraft 1.16.3 + +I hope the Fabric folks now realise this is gonna be a race of who can +update first :p. Either way, this was a very easy update - only changes +were due to unrelated Forge changes. ``` \ No newline at end of file From 2e527eb11e8f6b4581a16f15f94feaa9c1dbe04f Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 01:45:09 -0800 Subject: [PATCH 08/36] Fix additional `-` in docs --- patchwork.md | 7 +++++++ .../resources/data/computercraft/lua/rom/apis/window.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/patchwork.md b/patchwork.md index 44c0762f2..516d00658 100644 --- a/patchwork.md +++ b/patchwork.md @@ -45,4 +45,11 @@ Update to Minecraft 1.16.3 I hope the Fabric folks now realise this is gonna be a race of who can update first :p. Either way, this was a very easy update - only changes were due to unrelated Forge changes. +``` + +``` +87393e8aef9ddfaca465d626ee7cff5ff499a7e8 +Fix additional `-` in docs + +Why isn't this automatically stripped! Bad squid. ``` \ No newline at end of file diff --git a/src/main/resources/data/computercraft/lua/rom/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index d93533f03..ec112a991 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/window.lua @@ -440,7 +440,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end --- Get the buffered contents of a line in this window. - --- + -- -- @tparam number y The y position of the line to get. -- @treturn string The textual content of this line. -- @treturn string The text colours of this line, suitable for use with @{term.blit}. From 71d764f122b745cbea0ef3fe3dfe0717c32bca32 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 02:56:04 -0800 Subject: [PATCH 09/36] HTTP rules now allow filtering by port --- gradle.properties | 2 +- patchwork.md | 20 +++++++++++ .../dan200/computercraft/ComputerCraft.java | 4 +-- .../core/apis/http/CheckUrl.java | 9 ++--- .../core/apis/http/NetworkUtils.java | 18 +++++++++- .../core/apis/http/options/AddressRule.java | 27 ++++++++++----- .../apis/http/options/AddressRuleConfig.java | 8 +++-- .../core/apis/http/request/HttpRequest.java | 2 +- .../core/apis/http/websocket/Websocket.java | 2 +- .../core/apis/options/AddressRuleTest.java | 34 +++++++++++++++++++ 10 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java diff --git a/gradle.properties b/gradle.properties index 6e8772510..e010b4a16 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx1G mod_version=1.92.0 # Minecraft properties -mc_version=1.16.3 +mc_version=1.16.2 mappings_version=31 # Dependencies diff --git a/patchwork.md b/patchwork.md index 516d00658..4763fe0a9 100644 --- a/patchwork.md +++ b/patchwork.md @@ -52,4 +52,24 @@ were due to unrelated Forge changes. Fix additional `-` in docs Why isn't this automatically stripped! Bad squid. +``` + +``` +275ca58a82c627128a145a8754cbe32568536bd9 +HTTP rules now allow filtering by port + +The HTTP filtering system becomes even more complex! Though in this +case, it's pretty minimal, and definitely worth doing. + +For instance, the following rule will allow connecting to localhost on +port :8080. + + [[http.rules]] + host = "127.0.0.1" + port = 8080 + action = "allow" + + # Other rules as before. + +Closes #540 ``` \ No newline at end of file diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index a5d0ea5e8..68d49e580 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -126,10 +126,10 @@ public final class ComputerCraft implements ModInitializer { public static List buildHttpRulesFromConfig(String[] blacklist, String[] whitelist) { return Stream.concat(Stream.of(blacklist) - .map(x -> AddressRule.parse(x, Action.DENY.toPartial())) + .map( x -> AddressRule.parse( x, null, Action.DENY.toPartial())) .filter(Objects::nonNull), Stream.of(whitelist) - .map(x -> AddressRule.parse(x, Action.ALLOW.toPartial())) + .map( x -> AddressRule.parse( x, null, Action.ALLOW.toPartial())) .filter(Objects::nonNull)) .collect(Collectors.toList()); } diff --git a/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java b/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java index 2ba2b3ea4..7ff1421db 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java +++ b/src/main/java/dan200/computercraft/core/apis/http/CheckUrl.java @@ -21,14 +21,14 @@ public class CheckUrl extends Resource { private static final String EVENT = "http_check"; private final IAPIEnvironment environment; private final String address; - private final String host; + private final URI uri; private Future future; public CheckUrl(ResourceGroup limiter, IAPIEnvironment environment, String address, URI uri) { super(limiter); this.environment = environment; this.address = address; - this.host = uri.getHost(); + this.uri = uri; } public void run() { @@ -45,8 +45,9 @@ public class CheckUrl extends Resource { } try { - InetSocketAddress netAddress = NetworkUtils.getAddress(this.host, 80, false); - NetworkUtils.getOptions(this.host, netAddress); + boolean ssl = uri.getScheme().equalsIgnoreCase( "https" ); + InetSocketAddress netAddress = NetworkUtils.getAddress( uri, ssl ); + NetworkUtils.getOptions( uri.getHost(), netAddress ); if (this.tryClose()) { this.environment.queueEvent(EVENT, this.address, true); diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index a91c9a559..5619dbacb 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http; import java.net.InetSocketAddress; +import java.net.URI; import java.security.KeyStore; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; @@ -94,6 +95,21 @@ public final class NetworkUtils { } } + /** + * Create a {@link InetSocketAddress} from a {@link java.net.URI}. + * + * Note, this may require a DNS lookup, and so should not be executed on the main CC thread. + * + * @param uri The URI to fetch. + * @param ssl Whether to connect with SSL. This is used to find the default port if not otherwise specified. + * @return The resolved address. + * @throws HTTPRequestException If the host is not malformed. + */ + public static InetSocketAddress getAddress( URI uri, boolean ssl ) throws HTTPRequestException + { + return getAddress( uri.getHost(), uri.getPort(), ssl ); + } + /** * Create a {@link InetSocketAddress} from the resolved {@code host} and port. * @@ -125,7 +141,7 @@ public final class NetworkUtils { * @throws HTTPRequestException If the host is not permitted */ public static Options getOptions(String host, InetSocketAddress address) throws HTTPRequestException { - Options options = AddressRule.apply(ComputerCraft.httpRules, host, address.getAddress()); + Options options = AddressRule.apply( ComputerCraft.httpRules, host, address ); if (options.action == Action.DENY) { throw new HTTPRequestException("Domain not permitted"); } diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java index 6631193d3..c8d2017b8 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java @@ -8,6 +8,7 @@ package dan200.computercraft.core.apis.http.options; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.regex.Pattern; import javax.annotation.Nonnull; @@ -26,15 +27,22 @@ public final class AddressRule { public static final int WEBSOCKET_MESSAGE = 128 * 1024; private final HostRange ip; private final Pattern domainPattern; + private final Integer port; private final PartialOptions partial; - private AddressRule(@Nullable HostRange ip, @Nullable Pattern domainPattern, @Nonnull PartialOptions partial) { + private AddressRule( + @Nullable HostRange ip, + @Nullable Pattern domainPattern, + @Nullable Integer port, + @Nonnull PartialOptions partial ) + { this.ip = ip; this.domainPattern = domainPattern; this.partial = partial; + this.port = port; } @Nullable - public static AddressRule parse(String filter, @Nonnull PartialOptions partial) { + public static AddressRule parse( String filter, @Nullable Integer port, @Nonnull PartialOptions partial ) { int cidr = filter.indexOf('/'); if (cidr >= 0) { String addressStr = filter.substring(0, cidr); @@ -73,14 +81,14 @@ public final class AddressRule { size -= 8; } - return new AddressRule(new HostRange(minBytes, maxBytes), null, partial); + return new AddressRule(new HostRange(minBytes, maxBytes), null, port, partial); } else { Pattern pattern = Pattern.compile("^\\Q" + filter.replaceAll("\\*", "\\\\E.*\\\\Q") + "\\E$"); - return new AddressRule(null, pattern, partial); + return new AddressRule(null, pattern, port, partial); } } - public static Options apply(Iterable rules, String domain, InetAddress address) { + public static Options apply(Iterable rules, String domain, InetSocketAddress address) { PartialOptions options = null; boolean hasMany = false; @@ -108,11 +116,14 @@ public final class AddressRule { /** * Determine whether the given address matches a series of patterns. * - * @param domain The domain to match - * @param address The address to check. + * @param domain The domain to match + * @param socketAddress The address to check. * @return Whether it matches any of these patterns. */ - private boolean matches(String domain, InetAddress address) { + private boolean matches(String domain, InetSocketAddress socketAddress) { + InetAddress address = socketAddress.getAddress(); + if( port != null && port != socketAddress.getPort() ) return false; + if (this.domainPattern != null) { if (this.domainPattern.matcher(domain) .matches()) { diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java index 8dc11dca0..c520d9675 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java @@ -9,6 +9,8 @@ package dan200.computercraft.core.apis.http.options; public class AddressRuleConfig { // TODO haha config is gone, do fix + // TODO FIGURE OUT WHY THE HELL THE PREVIOUS GUY HAD TO COMMENT THIS OUT + // public static UnmodifiableConfig makeRule( String host, Action action ) // { // CommentedConfig config = InMemoryCommentedFormat.defaultInstance().createConfig( ConcurrentHashMap::new ); @@ -39,17 +41,19 @@ public class AddressRuleConfig { // { // String hostObj = get( builder, "host", String.class ).orElse( null ); // return hostObj != null && checkEnum( builder, "action", Action.class ) + // && check( builder, "port", Number.class ) // && check( builder, "timeout", Number.class ) // && check( builder, "max_upload", Number.class ) // && check( builder, "max_download", Number.class ) // && check( builder, "websocket_message", Number.class ) - // && AddressRule.parse( hostObj, PartialOptions.DEFAULT ) != null; + // && AddressRule.parse( hostObj, port, PartialOptions.DEFAULT ) != null; // } // // @Nullable // public static AddressRule parseRule( UnmodifiableConfig builder ) // { // String hostObj = get( builder, "host", String.class ).orElse( null ); + // Integer port = get( builder, "port", Number.class ).map( Number::intValue ).orElse( null ); // if( hostObj == null ) return null; // // Action action = getEnum( builder, "action", Action.class ).orElse( null ); @@ -66,7 +70,7 @@ public class AddressRuleConfig { // websocketMessage // ); // - // return AddressRule.parse( hostObj, options ); + // return AddressRule.parse( hostObj, port, options ); // } // // private static boolean check( UnmodifiableConfig config, String field, Class klass ) diff --git a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java index 6e6a315f5..7e7645004 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java +++ b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java @@ -131,7 +131,7 @@ public class HttpRequest extends Resource { try { boolean ssl = uri.getScheme() .equalsIgnoreCase("https"); - InetSocketAddress socketAddress = NetworkUtils.getAddress(uri.getHost(), uri.getPort(), ssl); + InetSocketAddress socketAddress = NetworkUtils.getAddress(uri, ssl); Options options = NetworkUtils.getOptions(uri.getHost(), socketAddress); SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null; diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java index 493ac3d99..1014c2e85 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java @@ -117,7 +117,7 @@ public class Websocket extends Resource { boolean ssl = this.uri.getScheme() .equalsIgnoreCase("wss"); - InetSocketAddress socketAddress = NetworkUtils.getAddress(this.uri.getHost(), this.uri.getPort(), ssl); + InetSocketAddress socketAddress = NetworkUtils.getAddress(uri, ssl); Options options = NetworkUtils.getOptions(this.uri.getHost(), socketAddress); SslContext sslContext = ssl ? NetworkUtils.getSslContext() : null; diff --git a/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java b/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java new file mode 100644 index 000000000..690cb37f5 --- /dev/null +++ b/src/test/java/dan200/computercraft/core/apis/options/AddressRuleTest.java @@ -0,0 +1,34 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ + +package dan200.computercraft.core.apis.http.options; + +import org.junit.jupiter.api.Test; + +import java.net.InetSocketAddress; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AddressRuleTest +{ + @Test + public void matchesPort() + { + Iterable rules = Collections.singletonList( AddressRule.parse( + "127.0.0.1", 8080, + new PartialOptions( Action.ALLOW, null, null, null, null ) + ) ); + + assertEquals( apply( rules, "localhost", 8080 ).action, Action.ALLOW ); + assertEquals( apply( rules, "localhost", 8081 ).action, Action.DENY ); + } + + private Options apply( Iterable rules, String host, int port ) + { + return AddressRule.apply( rules, host, new InetSocketAddress( host, port ) ); + } +} From d483a91459f6bf3dede6586e8790ce92e223bac0 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 03:08:41 -0800 Subject: [PATCH 10/36] Use tags to check if something is a dye --- patchwork.md | 11 ++++++++++- .../shared/peripheral/printer/ContainerPrinter.java | 3 +-- .../shared/peripheral/printer/TilePrinter.java | 12 +++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/patchwork.md b/patchwork.md index 4763fe0a9..465fe5822 100644 --- a/patchwork.md +++ b/patchwork.md @@ -72,4 +72,13 @@ port :8080. # Other rules as before. Closes #540 -``` \ No newline at end of file +``` + +The alterations in ColourUtils.java were not needed so they were not ported over +``` +6f868849ab2f264508e12c184cc56f2632aaf5bc +Use tags to check if something is a dye + +We half did this already, just needed to change a couple of checks. +Closes #541. +``` diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java index 52d3757c2..dd9e9d761 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java @@ -15,7 +15,6 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.SimpleInventory; -import net.minecraft.item.DyeItem; import net.minecraft.item.ItemStack; import net.minecraft.screen.ArrayPropertyDelegate; import net.minecraft.screen.PropertyDelegate; @@ -87,7 +86,7 @@ public class ContainerPrinter extends ScreenHandler { } } else { // Transfer from inventory to printer - if (stack.getItem() instanceof DyeItem) { + if( TilePrinter.isInk( stack ) ) { if (!this.insertItem(stack, 0, 1, false)) { return ItemStack.EMPTY; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java index a6dbfbbab..03fa0a1e9 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java @@ -278,8 +278,8 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent } } - private static boolean isInk(@Nonnull ItemStack stack) { - return stack.getItem() instanceof DyeItem; + static boolean isInk(@Nonnull ItemStack stack) { + return ColourUtils.getStackColour( stack ) != null; } @Nonnull @@ -380,9 +380,8 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent private boolean inputPage() { ItemStack inkStack = this.m_inventory.get(0); - if (!isInk(inkStack)) { - return false; - } + DyeColor dye = ColourUtils.getStackColour( inkStack ); + if( dye == null ) return false; for (int i = 1; i < 7; i++) { ItemStack paperStack = this.m_inventory.get(i); @@ -391,8 +390,7 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent } // Setup the new page - DyeColor dye = ColourUtils.getStackColour(inkStack); - this.m_page.setTextColour(dye != null ? dye.getId() : 15); + m_page.setTextColour( dye.getId() ); this.m_page.clear(); if (paperStack.getItem() instanceof ItemPrintout) { From 79534e5630926f71d9051e2cb389730cc4fa52a6 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 03:22:39 -0800 Subject: [PATCH 11/36] Fix incorrect open container check --- patchwork.md | 8 ++++++++ .../computercraft/shared/turtle/core/TurtlePlayer.java | 5 ++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/patchwork.md b/patchwork.md index 465fe5822..592354325 100644 --- a/patchwork.md +++ b/patchwork.md @@ -82,3 +82,11 @@ Use tags to check if something is a dye We half did this already, just needed to change a couple of checks. Closes #541. ``` + +``` +6cee4efcd3610536ee74330cd728f7371011e5a8 +Fix incorrect open container check + +Was this always broken, or did it happen in a Minecraft update? Don't +know, but it's a very silly mistake either way. Fixes #544 +``` diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 23e86ad76..41911b119 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -54,10 +54,9 @@ public final class TurtlePlayer extends FakePlayer { } private void setState(ITurtleAccess turtle) { - if (this.currentScreenHandler != null) { + if (this.currentScreenHandler != playerScreenHandler) { ComputerCraft.log.warn("Turtle has open container ({})", this.currentScreenHandler); - this.currentScreenHandler.close(this); - this.currentScreenHandler = null; + closeCurrentScreen(); } BlockPos position = turtle.getPosition(); From 75f2b91fefa86b12fd06c050cf7be6e74f927e55 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 03:23:58 -0800 Subject: [PATCH 12/36] Translations for Swedish --- patchwork.md | 7 ++ .../assets/computercraft/lang/sv_se.json | 77 ++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/patchwork.md b/patchwork.md index 592354325..d1eab9d2d 100644 --- a/patchwork.md +++ b/patchwork.md @@ -90,3 +90,10 @@ Fix incorrect open container check Was this always broken, or did it happen in a Minecraft update? Don't know, but it's a very silly mistake either way. Fixes #544 ``` + +``` +0832974725b2478c5227b81f82c35bbf03cf6aba +Translations for Swedish + +Co-authored-by: David Isaksson +``` diff --git a/src/main/resources/assets/computercraft/lang/sv_se.json b/src/main/resources/assets/computercraft/lang/sv_se.json index b71e4fecb..1afe76b56 100644 --- a/src/main/resources/assets/computercraft/lang/sv_se.json +++ b/src/main/resources/assets/computercraft/lang/sv_se.json @@ -1,14 +1,14 @@ { "block.computercraft.computer_normal": "Dator", "block.computercraft.computer_advanced": "Avancerad Dator", - "block.computercraft.computer_command": "Kommando Dator", + "block.computercraft.computer_command": "Kommandodator", "block.computercraft.disk_drive": "Diskettläsare", "block.computercraft.printer": "Skrivare", "block.computercraft.speaker": "Högtalare", "block.computercraft.monitor_normal": "Skärm", "block.computercraft.monitor_advanced": "Avancerad Skärm", "block.computercraft.wireless_modem_normal": "Trådlöst Modem", - "block.computercraft.wireless_modem_advanced": "Ender Modem", + "block.computercraft.wireless_modem_advanced": "Endermodem", "block.computercraft.wired_modem": "Trådat Modem", "block.computercraft.cable": "Nätverkskabel", "block.computercraft.wired_modem_full": "Trådat Modem", @@ -38,5 +38,76 @@ "upgrade.computercraft.speaker.adjective": "Högljudd", "chat.computercraft.wired_modem.peripheral_connected": "Kringutrustning \"%s\" är kopplad till nätverket", "chat.computercraft.wired_modem.peripheral_disconnected": "Kringutrustning \"%s\" är frånkopplad från nätverket", - "gui.computercraft.tooltip.copy": "Kopiera till urklipp" + "gui.computercraft.tooltip.copy": "Kopiera till urklipp", + "gui.computercraft.tooltip.disk_id": "Diskett-ID: %s", + "gui.computercraft.tooltip.computer_id": "Dator-ID: %s", + "tracking_field.computercraft.coroutines_dead.name": "Coroutines borttagna", + "tracking_field.computercraft.coroutines_created.name": "Coroutines skapade", + "tracking_field.computercraft.websocket_outgoing.name": "Websocket utgående", + "tracking_field.computercraft.websocket_incoming.name": "Websocket ingående", + "tracking_field.computercraft.http_download.name": "HTTP-nedladdning", + "tracking_field.computercraft.http_upload.name": "HTTP-uppladdning", + "tracking_field.computercraft.http.name": "HTTP-förfrågningar", + "tracking_field.computercraft.turtle.name": "Turtle-operationer", + "tracking_field.computercraft.fs.name": "Filsystemoperationer", + "tracking_field.computercraft.peripheral.name": "Samtal till kringutrustning", + "tracking_field.computercraft.server_time.name": "Serveraktivitetstid", + "tracking_field.computercraft.server_count.name": "Antal serveruppgifter", + "tracking_field.computercraft.max.name": "Max tid", + "tracking_field.computercraft.average.name": "Genomsnittlig tid", + "tracking_field.computercraft.total.name": "Total tid", + "tracking_field.computercraft.tasks.name": "Uppgifter", + "argument.computercraft.argument_expected": "Argument förväntas", + "argument.computercraft.tracking_field.no_field": "Okänt fält '%s'", + "argument.computercraft.computer.many_matching": "Flera datorer matchar '%s' (%s träffar)", + "argument.computercraft.computer.no_matching": "Inga datorer matchar '%s'", + "commands.computercraft.generic.additional_rows": "%d ytterligare rader…", + "commands.computercraft.generic.exception": "Ohanterat felfall (%s)", + "commands.computercraft.generic.no": "N", + "commands.computercraft.generic.yes": "J", + "commands.computercraft.generic.position": "%s, %s, %s", + "commands.computercraft.generic.no_position": "", + "commands.computercraft.queue.desc": "Skicka ett computer_command event till en kommandodator, skicka vidare ytterligare argument. Detta är mestadels utformat för kartmarkörer som fungerar som en mer datorvänlig version av /trigger. Alla spelare kan köra kommandot, vilket sannolikt skulle göras genom en textkomponents klick-event.", + "commands.computercraft.queue.synopsis": "Skicka ett computer_command event till en kommandodator", + "commands.computercraft.reload.done": "Konfiguration omladdad", + "commands.computercraft.reload.desc": "Ladda om ComputerCrafts konfigurationsfil", + "commands.computercraft.reload.synopsis": "Ladda om ComputerCrafts konfigurationsfil", + "commands.computercraft.track.dump.computer": "Dator", + "commands.computercraft.track.dump.no_timings": "Inga tidtagningar tillgängliga", + "commands.computercraft.track.dump.desc": "Dumpa de senaste resultaten av datorspårning.", + "commands.computercraft.track.dump.synopsis": "Dumpa de senaste spårningsresultaten", + "commands.computercraft.track.stop.not_enabled": "Spårar för tillfället inga datorer", + "commands.computercraft.track.stop.action": "Klicka för att stoppa spårning", + "commands.computercraft.track.stop.desc": "Stoppa spårning av alla datorers körtider och eventräkningar", + "commands.computercraft.track.stop.synopsis": "Stoppa spårning för alla datorer", + "commands.computercraft.track.start.stop": "Kör %s för att stoppa spårning och visa resultaten", + "commands.computercraft.track.start.desc": "Börja spåra alla dators körtider och eventräkningar. Detta kommer återställa resultaten från tidigare körningar.", + "commands.computercraft.track.start.synopsis": "Starta spårning för alla datorer", + "commands.computercraft.track.desc": "Spåra hur länge datorer exekverar, och även hur många event de hanterar. Detta presenterar information på liknande sätt som /forge track och kan vara användbart för att undersöka lagg.", + "commands.computercraft.track.synopsis": "Spåra körningstider för denna dator.", + "commands.computercraft.view.not_player": "Kan inte öppna terminalen för en ickespelare", + "commands.computercraft.view.action": "Titta på denna dator", + "commands.computercraft.view.desc": "Öppna datorns terminal för att möjligöra fjärrstyrning. Detta ger inte tillgång till turtlens inventory. Du kan ange en dators instans-id (t.ex. 123) eller dator-id (t.ex. #123).", + "commands.computercraft.view.synopsis": "Titta på datorns terminal.", + "commands.computercraft.tp.not_there": "Kan inte hitta datorn i världen", + "commands.computercraft.tp.not_player": "Kan inte öppna terminalen för en ickespelare", + "commands.computercraft.tp.action": "Teleportera till den här datorn", + "commands.computercraft.tp.desc": "Teleportera till datorns position. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.tp.synopsis": "Teleportera till en specifik dator.", + "commands.computercraft.turn_on.done": "Startade %s/%s datorer", + "commands.computercraft.turn_on.desc": "Starta de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.turn_on.synopsis": "Starta på datorer på distans.", + "commands.computercraft.shutdown.done": "Stängde av %s/%s datorer", + "commands.computercraft.dump.desc": "Visa status för alla datorer eller specifik information för en dator. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.shutdown.desc": "Stäng av de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.shutdown.synopsis": "Stäng av datorer på distans.", + "commands.computercraft.dump.action": "Visa mer information om den här datorn", + "commands.computercraft.dump.synopsis": "Visa status för datorer.", + "commands.computercraft.help.no_command": "Inget sådant kommando '%s'", + "commands.computercraft.help.no_children": "%s har inget underkommando", + "commands.computercraft.help.desc": "Visa detta hjälpmeddelande", + "commands.computercraft.help.synopsis": "Tillhandahåll hjälp för ett specifikt kommando", + "commands.computercraft.desc": "/computercraft kommandot tillhandahåller olika debugging- och administrationsverktyg för att kontrollera och interagera med datorer.", + "commands.computercraft.synopsis": "Olika kommandon för att kontrollera datorer.", + "itemGroup.computercraft": "ComputerCraft" } From 076d572831e3298ce3417590aa24c8f337cda430 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 03:25:08 -0800 Subject: [PATCH 13/36] Fix io.open documentation --- patchwork.md | 7 +++++++ src/main/resources/data/computercraft/lua/rom/apis/io.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/patchwork.md b/patchwork.md index d1eab9d2d..54031812c 100644 --- a/patchwork.md +++ b/patchwork.md @@ -97,3 +97,10 @@ Translations for Swedish Co-authored-by: David Isaksson ``` + +``` +84036d97d99efd8762e0170002060ae3471508bf +Fix io.open documentation + +Well, that was silly. +``` diff --git a/src/main/resources/data/computercraft/lua/rom/apis/io.lua b/src/main/resources/data/computercraft/lua/rom/apis/io.lua index 917faef7f..5ecbdf652 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/io.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/io.lua @@ -289,7 +289,7 @@ end -- The `mode` string can be any of the following: -- - **"r"**: Read mode -- - **"w"**: Write mode --- - **"w"**: Append mode +-- - **"a"**: Append mode -- -- The mode may also have a `b` at the end, which opens the file in "binary -- mode". This allows you to read binary files, as well as seek within a file. From 51ca5e620caa07d5a63e98af183552843596ecc1 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:22:01 -0800 Subject: [PATCH 14/36] Don't propagate adjacent redstone signals for computers (#549) [PatchWork] Also updated patchwork format information --- patchwork.md | 20 ++++++++++++++++++- .../shared/ComputerCraftRegistry.java | 10 ++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/patchwork.md b/patchwork.md index 54031812c..3a2038b67 100644 --- a/patchwork.md +++ b/patchwork.md @@ -9,7 +9,9 @@ SubScript // Desc of commit ``` If a edit that is present in CC:T is not needed, I will skip over it. -Any and all references to an issue number, are to be found on CC:T's repo. not this oen +Any and all references to an issue number, are to be found on CC:T's repo. + +Lines that are found above a commit in this log like this one, (excluding this one) are comments about how i had to implement things that are not a simple 1:1 (excluding fabric/forge differences) conversion ```md 5155e18de279a193c558aa029963486fd1294769 @@ -104,3 +106,19 @@ Fix io.open documentation Well, that was silly. ``` + +I set the default properties for computers as `Block.GLASS` and then set their strength to `2F` and their soundgroup to stone +``` +8472112fc1eaad18ed6ed2c6c62b040fe421e81a +Don't propagate adjacent redstone signals for computers (#549) + +Minecraft propagates "strong" redstone signals (such as those directly +from comparators or repeaters) through solid blocks. This includes +computers, which is a little annoying as it means one cannot feed +redstone wire from one side and a repeater from another. + +This changes computers to not propagate strong redstone signals, in the +same way transparent blocks like glass do. + +Closes #548. +``` diff --git a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java b/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java index 8dc8357b9..e2aa3763e 100644 --- a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java +++ b/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java @@ -55,6 +55,7 @@ import dan200.computercraft.shared.turtle.items.ItemTurtle; import dan200.computercraft.shared.turtle.upgrades.*; import dan200.computercraft.shared.util.FixedPointTileEntityType; +import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.Material; @@ -68,6 +69,7 @@ import net.minecraft.item.ItemGroup; import net.minecraft.item.Items; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.sound.BlockSoundGroup; import net.minecraft.util.Identifier; import net.minecraft.util.registry.Registry; @@ -121,8 +123,12 @@ public final class ComputerCraftRegistry { public static final BlockCable CABLE = register("cable", new BlockCable(emProperties())); private static Block.Settings properties() { - return FabricBlockSettings.copyOf(Blocks.STONE) - .strength(2); + //return FabricBlockSettings.copyOf(Blocks.GLASS) + // .strength(2); + return AbstractBlock.Settings.of(Material.GLASS) + .strength(2F) + .sounds(BlockSoundGroup.STONE) + .nonOpaque(); } private static Block.Settings turtleProperties() { From 54eac0a2f83136d65271bc52593bf4148e2a85ed Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:27:15 -0800 Subject: [PATCH 15/36] CC:R 1.93 --- README.md | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7d9aeb2aa..cffe6d32f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ # This is a Work In Progress Port *it runs and works-ish* -## Reached Parity with CC:T 1.92.0 +## Reached Parity with CC:T 1.93.0 THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T -The changelog is located at [PatchWork.md](patchwork.md) \ No newline at end of file +The changelog is located at [PatchWork.md](patchwork.md) diff --git a/gradle.properties b/gradle.properties index e010b4a16..e19c16044 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Mod properties -mod_version=1.92.0 +mod_version=1.93.0 # Minecraft properties mc_version=1.16.2 From 08b3dbbad513a48e3a12b718e00a24a5f216d9b1 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:38:52 -0800 Subject: [PATCH 16/36] Fix My Docs, Update Patchwork Format --- patchwork.md | 10 ++++++++++ .../computercraft/lua/rom/modules/main/cc/pretty.lua | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/patchwork.md b/patchwork.md index 3a2038b67..bf4280921 100644 --- a/patchwork.md +++ b/patchwork.md @@ -11,6 +11,8 @@ SubScript // Desc of commit If a edit that is present in CC:T is not needed, I will skip over it. Any and all references to an issue number, are to be found on CC:T's repo. +Any commit that starts with `[Patchwork]` are purely edits made by my hand, and not based on other commits from CC:T, this is to help differentiate my changes from the official changes + Lines that are found above a commit in this log like this one, (excluding this one) are comments about how i had to implement things that are not a simple 1:1 (excluding fabric/forge differences) conversion ```md @@ -122,3 +124,11 @@ same way transparent blocks like glass do. Closes #548. ``` + +``` +30d35883b83831900b34040f0131c7e06f5c3e52 +Fix my docs + +Thanks @plt-hokusai. Kinda embarrassing this slipped through - I +evidently need to lint examples too. +``` diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua index b934aa995..a62a6f9f1 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -13,7 +13,7 @@ -- @module cc.pretty -- @usage Print a table to the terminal -- local pretty = require "cc.pretty" --- pretty.write(pretty.dump({ 1, 2, 3 })) +-- pretty.write(pretty.pretty({ 1, 2, 3 })) -- -- @usage Build a custom document and display it -- local pretty = require "cc.pretty" From 74ad9348895822085a342776f7b1c7b92556e806 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:41:26 -0800 Subject: [PATCH 17/36] Add color table to docs (#553) --- patchwork.md | 5 + .../computercraft/lua/rom/apis/colors.lua | 158 ++++++++++++++++-- 2 files changed, 147 insertions(+), 16 deletions(-) diff --git a/patchwork.md b/patchwork.md index bf4280921..b0e8deb6b 100644 --- a/patchwork.md +++ b/patchwork.md @@ -132,3 +132,8 @@ Fix my docs Thanks @plt-hokusai. Kinda embarrassing this slipped through - I evidently need to lint examples too. ``` + +``` +34a2c835d412c0d9e1fb20a42b7f2cd2738289c7 +Add color table to docs (#553) +``` diff --git a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua index a941fa287..211e9975c 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua @@ -1,15 +1,137 @@ ---- The Colors API allows you to manipulate sets of colors. --- --- This is useful in conjunction with Bundled Cables from the RedPower mod, --- RedNet Cables from the MineFactory Reloaded mod, and colors on Advanced --- Computers and Advanced Monitors. --- --- For the non-American English version just replace @{colors} with @{colours} --- and it will use the other API, colours which is exactly the same, except in --- British English (e.g. @{colors.gray} is spelt @{colours.grey}). --- --- @see colours --- @module colors +--[[- The Colors API allows you to manipulate sets of colors. + +This is useful in conjunction with Bundled Cables from the RedPower mod, RedNet +Cables from the MineFactory Reloaded mod, and colors on Advanced Computers and +Advanced Monitors. + +For the non-American English version just replace @{colors} with @{colours} and +it will use the other API, colours which is exactly the same, except in British +English (e.g. @{colors.gray} is spelt @{colours.grey}). + +On basic terminals (such as the Computer and Monitor), all the colors are +converted to grayscale. This means you can still use all 16 colors on the +screen, but they will appear as the nearest tint of gray. You can check if a +terminal supports color by using the function @{term.isColor}. + +Grayscale colors are calculated by taking the average of the three components, +i.e. `(red + green + blue) / 3`. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Default Colors
ColorValueDefault Palette Color
DecHexPaint/BlitPreviewHexRGBGrayscale
colors.white10x10#F0F0F0240, 240, 240
colors.orange20x21#F2B233242, 178, 51
colors.magenta40x42#E57FD8229, 127, 216
colors.lightBlue80x83#99B2F2153, 178, 242
colors.yellow160x104#DEDE6C222, 222, 108
colors.lime320x205#7FCC19127, 204, 25
colors.pink640x406#F2B2CC242, 178, 204
colors.gray1280x807#4C4C4C76, 76, 76
colors.lightGray2560x1008#999999153, 153, 153
colors.cyan5120x2009#4C99B276, 153, 178
colors.purple10240x400a#B266E5178, 102, 229
colors.blue20480x800b#3366CC51, 102, 204
colors.brown40960x1000c#7F664C127, 102, 76
colors.green81920x2000d#57A64E87, 166, 78
colors.red163840x4000e#CC4C4C204, 76, 76
colors.black327680x8000f#11111117, 17, 17
+ +@see colours +@module colors +]] local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -37,7 +159,7 @@ yellow = 0x10 -- terminal colour of #7FCC19. lime = 0x20 ---- Pink. Written as `6` in paint files and @{term.blit}, has a default +--- Pink: Written as `6` in paint files and @{term.blit}, has a default -- terminal colour of #F2B2CC. pink = 0x40 @@ -77,7 +199,8 @@ red = 0x4000 -- terminal colour of #191919. black = 0x8000 ---- Combines a set of colors (or sets of colors) into a larger set. +--- Combines a set of colors (or sets of colors) into a larger set. Useful for +-- Bundled Cables. -- -- @tparam number ... The colors to combine. -- @treturn number The union of the color sets given in `...` @@ -96,7 +219,8 @@ function combine(...) return r end ---- Removes one or more colors (or sets of colors) from an initial set. +--- Removes one or more colors (or sets of colors) from an initial set. Useful +-- for Bundled Cables. -- -- Each parameter beyond the first may be a single color or may be a set of -- colors (in the latter case, all colors in the set are removed from the @@ -121,7 +245,9 @@ function subtract(colors, ...) return r end ---- Tests whether `color` is contained within `colors`. +--- Tests whether `color` is contained within `colors`. Useful for Bundled +-- Cables. +-- -- -- @tparam number colors A color, or color set -- @tparam number color A color or set of colors that `colors` should contain. From 6b493274626c49b086b24191d147d19d46366903 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:45:57 -0800 Subject: [PATCH 18/36] Document remaining OS functions (#554) --- patchwork.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/patchwork.md b/patchwork.md index b0e8deb6b..c60da186d 100644 --- a/patchwork.md +++ b/patchwork.md @@ -137,3 +137,13 @@ evidently need to lint examples too. 34a2c835d412c0d9e1fb20a42b7f2cd2738289c7 Add color table to docs (#553) ``` + +All API Documentation updates, +`Not Needed` for this repo. +``` +93068402a2ffec00eedb8fe2d859ebdc005a1989 +Document remaining OS functions (#554) + +01d81cb91da938836f953b290ad6b8fc87cb7e35 +Update illuaminate CSS for deprecation (#556) +``` From aa4ec53bb69e925367561dc158378eac94e38654 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:50:57 -0800 Subject: [PATCH 19/36] Bump JEI/crafttweaker versions --- patchwork.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/patchwork.md b/patchwork.md index c60da186d..6826a6680 100644 --- a/patchwork.md +++ b/patchwork.md @@ -147,3 +147,11 @@ Document remaining OS functions (#554) 01d81cb91da938836f953b290ad6b8fc87cb7e35 Update illuaminate CSS for deprecation (#556) ``` + +``` +Not Needed +4766833cf2d041ed179529eecb9402ad09b2b79b +Bump JEI/crafttweaker versions + +In my defence, they weren't out when I started the 1.15 update. +``` From 0e5fd4e8e06654dc2d544e98c8f2f41aba599468 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 10:56:52 -0800 Subject: [PATCH 20/36] Fix TBO norm issues on old GPUs --- patchwork.md | 5 +++++ .../shared/peripheral/monitor/ClientMonitor.java | 2 +- .../resources/assets/computercraft/shaders/monitor.frag | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/patchwork.md b/patchwork.md index 6826a6680..c90a6de22 100644 --- a/patchwork.md +++ b/patchwork.md @@ -155,3 +155,8 @@ Bump JEI/crafttweaker versions In my defence, they weren't out when I started the 1.15 update. ``` + +``` +bf6053906dc6a3c7b0d40d5b097e745dce1f33bc +Fix TBO norm issues on old GPUs +``` diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java index 05b5cb99b..5fce19bc1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -80,7 +80,7 @@ public final class ClientMonitor extends ClientTerminal { GL15.glBufferData(GL31.GL_TEXTURE_BUFFER, 0, GL15.GL_STATIC_DRAW); this.tboTexture = GlStateManager.genTextures(); GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, this.tboTexture); - GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL30.GL_R8, this.tboBuffer); + GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, this.tboBuffer); GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, 0); GlStateManager.bindBuffers(GL31.GL_TEXTURE_BUFFER, 0); diff --git a/src/main/resources/assets/computercraft/shaders/monitor.frag b/src/main/resources/assets/computercraft/shaders/monitor.frag index b0b7b49ed..f19a6a9eb 100644 --- a/src/main/resources/assets/computercraft/shaders/monitor.frag +++ b/src/main/resources/assets/computercraft/shaders/monitor.frag @@ -6,7 +6,7 @@ uniform sampler2D u_font; uniform int u_width; uniform int u_height; -uniform samplerBuffer u_tbo; +uniform usamplerBuffer u_tbo; uniform vec3 u_palette[16]; in vec2 f_pos; @@ -30,9 +30,9 @@ void main() { vec2 outside = step(vec2(0.0, 0.0), vec2(cell)) * step(vec2(cell), vec2(float(u_width) - 1.0, float(u_height) - 1.0)); float mult = outside.x * outside.y; - int character = int(texelFetch(u_tbo, index).r * 255.0); - int fg = int(texelFetch(u_tbo, index + 1).r * 255.0); - int bg = int(texelFetch(u_tbo, index + 2).r * 255.0); + int character = int(texelFetch(u_tbo, index).r); + int fg = int(texelFetch(u_tbo, index + 1).r); + int bg = int(texelFetch(u_tbo, index + 2).r); vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT); vec4 img = texture(u_font, (texture_corner(character) + pos) / 256.0); From 89a195ec06638bf359e25a5a9956119c3bbf8949 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 11:04:40 -0800 Subject: [PATCH 21/36] CC:R 1.93.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e19c16044..e39d02601 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Mod properties -mod_version=1.93.0 +mod_version=1.93.1 # Minecraft properties mc_version=1.16.2 From 27a2c063b9161fca4dbfa78a432474f131ca1d84 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 11:50:33 -0800 Subject: [PATCH 22/36] Update configuration to match latest illuaminate --- patchwork.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/patchwork.md b/patchwork.md index c90a6de22..26cb59266 100644 --- a/patchwork.md +++ b/patchwork.md @@ -160,3 +160,11 @@ In my defence, they weren't out when I started the 1.15 update. bf6053906dc6a3c7b0d40d5b097e745dce1f33bc Fix TBO norm issues on old GPUs ``` + +``` +Not Needed +113b560a201dbdea9de2a2ef536bcce1d6e51978 +Update configuration to match latest illuaminate + +Ooooooh, it's all fancy now. Well, that or horrifically broken. +``` From 0aa6ac05a08e61297ecf821a366e5bb1a0235b9f Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 11:56:22 -0800 Subject: [PATCH 23/36] Add function to get window visibility --- patchwork.md | 9 ++++ .../computercraft/lua/rom/apis/window.lua | 10 +++- .../test-rom/spec/apis/window_spec.lua | 50 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/patchwork.md b/patchwork.md index 26cb59266..10364757c 100644 --- a/patchwork.md +++ b/patchwork.md @@ -168,3 +168,12 @@ Update configuration to match latest illuaminate Ooooooh, it's all fancy now. Well, that or horrifically broken. ``` + +``` +c334423d42ba3b653ac3a8c27bce7970457f8f96 +Add function to get window visibility + +Closes #562 + +Co-authored-by: devomaa +``` diff --git a/src/main/resources/data/computercraft/lua/rom/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index ec112a991..684d54f9c 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/window.lua @@ -125,7 +125,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- Helper functions local function updateCursorPos() if nCursorX >= 1 and nCursorY >= 1 and - nCursorX <= nWidth and nCursorY <= nHeight then + nCursorX <= nWidth and nCursorY <= nHeight then parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1) else parent.setCursorPos(0, 0) @@ -474,6 +474,14 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end end + --- Get whether this window is visible. Invisible windows will not be + -- drawn to the screen until they are made visible again. + -- + -- @treturn boolean Whether this window is visible. + -- @see Window:setVisible + function window.isVisible() + return bVisible + end --- Draw this window. This does nothing if the window is not visible. -- -- @see Window:setVisible diff --git a/src/test/resources/test-rom/spec/apis/window_spec.lua b/src/test/resources/test-rom/spec/apis/window_spec.lua index 16fe229e1..d1861eef2 100644 --- a/src/test/resources/test-rom/spec/apis/window_spec.lua +++ b/src/test/resources/test-rom/spec/apis/window_spec.lua @@ -118,6 +118,56 @@ describe("The window library", function() expect.error(w.reposition, 1, 1, false, 1):eq("bad argument #3 (expected number, got boolean)") expect.error(w.reposition, 1, 1, nil, 1):eq("bad argument #3 (expected number, got nil)") expect.error(w.reposition, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)") + expect.error(w.reposition, 1, 1, 1, 1, true):eq("bad argument #5 (expected table, got boolean)") + end) + + it("can change the buffer", function() + local a, b = mk(), mk() + local target = window.create(a, 1, 1, a.getSize()) + + target.write("Test") + expect((a.getLine(1))):equal("Test ") + expect({ a.getCursorPos() }):same { 5, 1 } + + target.reposition(1, 1, nil, nil, b) + + target.redraw() + expect((a.getLine(1))):equal("Test ") + expect({ a.getCursorPos() }):same { 5, 1 } + + target.setCursorPos(1, 1) target.write("More") + expect((a.getLine(1))):equal("Test ") + expect((b.getLine(1))):equal("More ") + end) + end) + + describe("Window.getLine", function() + it("validates arguments", function() + local w = mk() + w.getLine(1) + local _, y = w.getSize() + expect.error(w.getLine, nil):eq("bad argument #1 (expected number, got nil)") + expect.error(w.getLine, 0):eq("Line is out of range.") + expect.error(w.getLine, y + 1):eq("Line is out of range.") + end) + + it("provides a line's contents", function() + local w = mk() + w.blit("test", "aaaa", "4444") + expect({ w.getLine(1) }):same { "test ", "aaaa0", "4444f" } + end) + end) + describe("Window.setVisible", function() + it("validates arguments", function() + local w = mk() + expect.error(w.setVisible, nil):eq("bad argument #1 (expected boolean, got nil)") + end) + end) + describe("Window.isVisible", function() + it("gets window visibility", function() + local w = mk() + w.setVisible(false) + expect(w.isVisible()):same(false) end) end) end) From 7326d1110dfdce0c9ed6c6f73d0dea7b96caf248 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 12:17:03 -0800 Subject: [PATCH 24/36] Make generic peripherals on by default --- patchwork.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/patchwork.md b/patchwork.md index 10364757c..7ee84e155 100644 --- a/patchwork.md +++ b/patchwork.md @@ -177,3 +177,26 @@ Closes #562 Co-authored-by: devomaa ``` + +[WARN] Could not implement changes to the following files +* `src/main/java/dan200/computercraft/ComputerCraft.java` < Structure too different, cannot find equivalent to alter +* `src/main/java/dan200/computercraft/shared/Config.java` < Files Does not exist in this repo +``` +84a6bb1cf3b0668ddc7d8c409a2477a42390e3f7 +Make generic peripherals on by default + +This is a long way away from "feature complete" as it were. However, +it's definitely at a point where it's suitable for general usage - I'm +happy with the API, and don't think I'm going to be breaking things any +time soon. + +That said, things aren't exposed yet for Java-side public consumption. I +was kinda waiting until working on Plethora to actually do that, but not +sure if/when that'll happen. + +If someone else wants to work on an integration mod (or just adding +integrations for their own mod), do get in touch and I can work out how +to expose this. + +Closes #452 +``` From 5d91491ec740e8ea5e6ea50bb0ecc7ba2e6f6a41 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 12:25:13 -0800 Subject: [PATCH 25/36] Remove superfluous imports --- patchwork.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/patchwork.md b/patchwork.md index 7ee84e155..8ebdd3df1 100644 --- a/patchwork.md +++ b/patchwork.md @@ -200,3 +200,11 @@ to expose this. Closes #452 ``` + +``` +Not Needed +6aae4e576621090840724e094aa25e51696530fc +Remove superfluous imports + +Hah, this is embarassing +``` From 592b83e7840266c6fae3b0994f4871164c4d83b5 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 17:32:26 -0800 Subject: [PATCH 26/36] [TODO] Fix players not getting advancements when they own turtles --- patchwork.md | 19 +++++++++ .../api/turtle/ITurtleAccess.java | 2 +- .../shared/turtle/core/TurtleBrain.java | 3 +- .../shared/turtle/core/TurtlePlayer.java | 42 +++++++++++++++---- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/patchwork.md b/patchwork.md index 8ebdd3df1..eee0c1131 100644 --- a/patchwork.md +++ b/patchwork.md @@ -208,3 +208,22 @@ Remove superfluous imports Hah, this is embarassing ``` + +[TODO] [M3R1-01] Code has been applied, players still dont get achievments +``` +f6160bdc57b3d9850607c2c7c2ce9734b4963478 +Fix players not getting advancements when they own turtles + +When we construct a new ServerPlayerEntity (and thus TurtlePlayer), we +get the current (global) advancement state and call .setPlayer() on it. + +As grantCriterion blocks FakePlayers from getting advancements, this +means a player will no longer receive any advancements, as the "wrong" +player object is being consulted. + +As a temporary work around, we attempt to restore the previous player to +the advancement store. I'll try to upstream something into Forge to +resolve this properly. + +Fixes #564 +``` diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java index f68182c20..a0ddbe9b7 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java @@ -135,7 +135,7 @@ public interface ITurtleAccess { * * @return This turtle's owner. */ - @Nonnull + @Nullable GameProfile getOwningPlayer(); /** diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index e4327fe94..c1fdc2a5d 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -19,6 +19,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import com.google.common.base.Objects; import com.mojang.authlib.GameProfile; @@ -341,7 +342,7 @@ public class TurtleBrain implements ITurtleAccess { } } - @Nonnull + @Nullable @Override public GameProfile getOwningPlayer() { return this.m_owningPlayer; diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 41911b119..c644bb643 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -33,6 +33,7 @@ import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.NamedScreenHandlerFactory; import net.minecraft.server.world.ServerWorld; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; @@ -42,13 +43,33 @@ import net.minecraft.util.math.Vec3d; @SuppressWarnings ("EntityConstructor") public final class TurtlePlayer extends FakePlayer { private static final GameProfile DEFAULT_PROFILE = new GameProfile(UUID.fromString("0d0c4ca0-4ff1-11e4-916c-0800200c9a66"), "[ComputerCraft]"); - - private TurtlePlayer(ITurtleAccess turtle) { - super((ServerWorld) turtle.getWorld(), getProfile(turtle.getOwningPlayer())); - this.networkHandler = new FakeNetHandler(this); - this.setState(turtle); +// TODO [M3R1-01] Fix Turtle not giving player achievement for actions + private TurtlePlayer( ServerWorld world, GameProfile name ) + { + super( world, name ); } + private static TurtlePlayer create( ITurtleAccess turtle ) + { + ServerWorld world = (ServerWorld) turtle.getWorld(); + GameProfile profile = turtle.getOwningPlayer(); + + TurtlePlayer player = new TurtlePlayer( world, getProfile( profile ) ); + player.networkHandler = new FakeNetHandler( player ); + player.setState( turtle ); + + if( profile != null && profile.getId() != null ) + { + // Constructing a player overrides the "active player" variable in advancements. As fake players cannot + // get advancements, this prevents a normal player who has placed a turtle from getting advancements. + // We try to locate the "actual" player and restore them. + ServerPlayerEntity actualPlayer = world.getServer().getPlayerManager().getPlayer(player.getUuid()); + if( actualPlayer != null ) player.getAdvancementTracker().setOwner(actualPlayer); + } + + return player; + } + private static GameProfile getProfile(@Nullable GameProfile profile) { return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE; } @@ -70,14 +91,17 @@ public final class TurtlePlayer extends FakePlayer { } public static TurtlePlayer get(ITurtleAccess access) { - if (!(access instanceof TurtleBrain)) { - return new TurtlePlayer(access); - } + ServerWorld world = (ServerWorld) access.getWorld(); + if( !(access instanceof TurtleBrain) ) return create( access ); + + /*if (!(access instanceof TurtleBrain)) { + return new TurtlePlayer(world, access.getOwningPlayer()); + }*/ TurtleBrain brain = (TurtleBrain) access; TurtlePlayer player = brain.m_cachedPlayer; if (player == null || player.getGameProfile() != getProfile(access.getOwningPlayer()) || player.getEntityWorld() != access.getWorld()) { - player = brain.m_cachedPlayer = new TurtlePlayer(brain); + player = brain.m_cachedPlayer = create(brain); } else { player.setState(access); } From 0240ce50ce2be14493726a48d465ade6f4324b15 Mon Sep 17 00:00:00 2001 From: Merith-TK Date: Mon, 22 Feb 2021 17:34:20 -0800 Subject: [PATCH 27/36] Bump cct-javadoc version --- patchwork.md | 7 +++++++ .../computercraft/shared/computer/apis/CommandAPI.java | 1 - .../shared/peripheral/diskdrive/DiskDrivePeripheral.java | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/patchwork.md b/patchwork.md index eee0c1131..631a6cf8f 100644 --- a/patchwork.md +++ b/patchwork.md @@ -227,3 +227,10 @@ resolve this properly. Fixes #564 ``` + +``` +17a932920711a5c0361a5048c9e0a5e7a58e6364 +Bump cct-javadoc version + +Documentation will now be sorted (somewhat) correctly! +``` diff --git a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index 02cb8ab44..ae5955e70 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -117,7 +117,6 @@ public class CommandAPI implements ILuaAPI { * @param command The command to execute. * @return The "task id". When this command has been executed, it will queue a `task_complete` event with a matching id. * @throws LuaException (hidden) If the task cannot be created. - * @cc.tparam string command The command to execute. * @cc.usage Asynchronously sets the block above the computer to stone. *
      *     commands.execAsync("~ ~1 ~ minecraft:stone")
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java
index d1a6631fd..f5065e6cc 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java
@@ -155,7 +155,8 @@ public class DiskDrivePeripheral implements IPeripheral {
     /**
      * Returns the title of the inserted audio disk.
      *
-     * @return The title of the audio, or {@code nil} if no audio disk is inserted.
+     * @return The title of the audio, or {@code false} if no audio disk is inserted.
+     * @cc.treturn string|nil|false The title of the audio, {@code false} if no disk is inserted, or {@code nil} if the disk has no audio.
      */
     @LuaFunction
     @Nullable

From feda08b9154ee828acc4aaee16b6c327fb0b9197 Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Mon, 22 Feb 2021 17:43:54 -0800
Subject: [PATCH 28/36] Draw in-hand pocket computers with blending

---
 patchwork.md                                           | 10 ++++++++++
 .../client/render/ItemPocketRenderer.java              |  2 +-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/patchwork.md b/patchwork.md
index 631a6cf8f..04a7fec16 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -234,3 +234,13 @@ Bump cct-javadoc version
 
 Documentation will now be sorted (somewhat) correctly!
 ```
+
+```
+a6fcfb6af2fc1bef8ca3a19122c9267549202424
+Draw in-hand pocket computers with blending
+
+It might be worth switching to RenderTypes here, rather than a pure
+Tesselator, but this'll do for now.
+
+Fixes Zundrel/cc-tweaked-fabric#20.
+```
diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
index 7d77764b5..85d74e169 100644
--- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
@@ -95,6 +95,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer {
     }
 
     private static void renderFrame(Matrix4f transform, ComputerFamily family, int colour, int width, int height) {
+        RenderSystem.enableBlend();
         MinecraftClient.getInstance()
                        .getTextureManager()
                        .bindTexture(colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture(family));
@@ -113,7 +114,6 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer {
     }
 
     private static void renderLight(Matrix4f transform, int colour, int width, int height) {
-        RenderSystem.enableBlend();
         RenderSystem.disableTexture();
 
         float r = ((colour >>> 16) & 0xFF) / 255.0f;

From eef36e13586cb4f80889e33723c2466490b4fabc Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Mon, 22 Feb 2021 17:45:44 -0800
Subject: [PATCH 29/36] Various SNBT parsing improvements

---
 patchwork.md                                  | 12 ++++++++++++
 .../computercraft/lua/rom/apis/textutils.lua  | 19 +++++++++++++------
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/patchwork.md b/patchwork.md
index 04a7fec16..1f628cf6a 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -244,3 +244,15 @@ Tesselator, but this'll do for now.
 
 Fixes Zundrel/cc-tweaked-fabric#20.
 ```
+
+``` 
+c58441b29c3715f092e7f3747bb3ec65ae5a3d29
+Various SNBT parsing improvements
+
+Correctly handle:
+ - Typed arrays ([I; 1, 2, 3])
+ - All suffixed numbers (1.2d)
+ - Single-quoted strings
+
+Fixes #559
+```
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
index 576c6755e..d7aeb9e67 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
@@ -453,13 +453,13 @@ do
         error_at(pos, "Unexpected %s, expected %s.", actual, exp)
     end
 
-    local function parse_string(str, pos)
+    local function parse_string(str, pos, terminate)
         local buf, n = {}, 1
 
         while true do
             local c = sub(str, pos, pos)
             if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end
-            if c == '"' then break end
+            if c == terminate then break end
 
             if c == '\\' then
                 -- Handle the various escapes
@@ -485,13 +485,13 @@ do
         return concat(buf, "", 1, n - 1), pos + 1
     end
 
-    local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
+    local num_types = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
     local function parse_number(str, pos, opts)
         local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos)
         local val = tonumber(num_str)
         if not val then error_at(pos, "Malformed number %q.", num_str) end
 
-        if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end
+        if opts.nbt_style and num_types[sub(str, last + 1, last + 1)] then return val, last + 2 end
 
         return val, last + 1
     end
@@ -501,9 +501,11 @@ do
         return val, last + 1
     end
 
+    local arr_types = { I = true, L = true, B = true }
     local function decode_impl(str, pos, opts)
         local c = sub(str, pos, pos)
-        if c == '"' then return parse_string(str, pos + 1)
+        if c == '"' then return parse_string(str, pos + 1, '"')
+        elseif c == "'" and opts.nbt_style then return parse_string(str, pos + 1, "\'")
         elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts)
         elseif c == "t" then
             if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end
@@ -560,6 +562,11 @@ do
             pos = skip(str, pos + 1)
             c = sub(str, pos, pos)
 
+            if arr_types[c] and sub(str, pos + 1, pos + 1) == ";" and opts.nbt_style then
+                pos = skip(str, pos + 2)
+                c = sub(str, pos, pos)
+            end
+
             if c == "" then return expected(pos, c, "']'") end
             if c == "]" then return empty_json_array, pos + 1 end
 
@@ -712,7 +719,7 @@ function urlEncode(str)
             else
                 -- Non-ASCII (encode as UTF-8)
                 return
-                    string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
+                string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
                     string.format("%%%02X", 128 + bit32.band(n, 63))
             end
         end)

From 457a863842ee4a7b2726a872abbe1b85fa41b4da Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Mon, 22 Feb 2021 18:09:31 -0800
Subject: [PATCH 30/36] Dont fail when codecov is being finicky

---
 .github/workflows/main-ci.yml | 50 +++++++++++++++++++++++++++++++++++
 patchwork.md                  |  5 ++++
 2 files changed, 55 insertions(+)
 create mode 100644 .github/workflows/main-ci.yml

diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml
new file mode 100644
index 000000000..9bb7fcdf5
--- /dev/null
+++ b/.github/workflows/main-ci.yml
@@ -0,0 +1,50 @@
+name: Build
+
+on: [push, pull_request]
+
+jobs:
+    build:
+        name: Build
+        runs-on: ubuntu-latest
+
+        steps:
+            - uses: actions/checkout@v2
+
+            - name: Set up Java 8
+              uses: actions/setup-java@v1
+              with:
+                  java-version: 8
+
+            - name: Cache gradle dependencies
+              uses: actions/cache@v1
+              with:
+                  path: ~/.gradle/caches
+                  key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties') }}
+                  restore-keys: |
+                      ${{ runner.os }}-gradle-
+
+            - name: Build with Gradle
+              run: ./gradlew build --no-daemon || ./gradlew build --no-daemon
+
+            - name: Upload Jar
+              uses: actions/upload-artifact@v1
+              with:
+                  name: CC-Tweaked
+                  path: build/libs
+
+            - name: Upload Coverage
+              run: bash <(curl -s https://codecov.io/bash)
+              continue-on-error: true
+
+            - name: Generate Java documentation stubs
+              run: ./gradlew luaJavadoc --no-daemon
+
+            - name: Lint Lua code
+              run: |
+                  test -d bin || mkdir bin
+                  test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
+                  chmod +x bin/illuaminate
+                  bin/illuaminate lint
+
+            - name: Check whitespace
+              run: python3 tools/check-lines.py
diff --git a/patchwork.md b/patchwork.md
index 1f628cf6a..bd9e7338f 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -256,3 +256,8 @@ Correctly handle:
 
 Fixes #559
 ```
+
+``` 
+e2a635b6e5f5942f999213434054e06833c5cb06
+Dont fail when codecov is being finicky
+```

From 7e65c6b25cd59c0033aebf9977e0c573749023be Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Mon, 22 Feb 2021 18:11:27 -0800
Subject: [PATCH 31/36] Fix JSON objects failing to pass

---
 patchwork.md                                              | 8 ++++++++
 .../data/computercraft/lua/rom/apis/textutils.lua         | 2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/patchwork.md b/patchwork.md
index bd9e7338f..83a4ec16a 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -261,3 +261,11 @@ Fixes #559
 e2a635b6e5f5942f999213434054e06833c5cb06
 Dont fail when codecov is being finicky
 ```
+
+```
+666e83cf4fd0eb327f465d5b919a708790f99b00
+Fix JSON objects failing to pass
+
+Maybe I should run the whole test suite, not just the things I think
+matter? Nah....
+```
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
index d7aeb9e67..358557b55 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
@@ -530,7 +530,7 @@ do
 
             while true do
                 local key, value
-                if c == "\"" then key, pos = parse_string(str, pos + 1)
+                if c == "\"" then key, pos = parse_string(str, pos + 1, "\"")
                 elseif opts.nbt_style then key, pos = parse_ident(str, pos)
                 else return expected(pos, c, "object key")
                 end

From ac452582c1477bd4b6fbbabcdbbb5c2d5364e170 Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Mon, 22 Feb 2021 18:49:39 -0800
Subject: [PATCH 32/36] Use blit to draw boxes, add colors.toBlit (#570)

---
 patchwork.md                                  |   6 +
 .../computercraft/lua/rom/apis/colors.lua     |  29 ++++-
 .../computercraft/lua/rom/apis/paintutils.lua |  98 ++++++++---------
 .../test-rom/spec/apis/colors_spec.lua        |  16 +++
 .../test-rom/spec/apis/paintutils_spec.lua    | 103 ++++++++++++++++++
 5 files changed, 195 insertions(+), 57 deletions(-)

diff --git a/patchwork.md b/patchwork.md
index 83a4ec16a..1fb580af8 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -269,3 +269,9 @@ Fix JSON objects failing to pass
 Maybe I should run the whole test suite, not just the things I think
 matter? Nah....
 ```
+
+``` 
+741adfa7bb2b950d2851c3f0072d6a4769f22773
+
+Use blit to draw boxes, add colors.toBlit (#570)
+```
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
index 211e9975c..2a7ca763e 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
@@ -196,7 +196,7 @@ green = 0x2000
 red = 0x4000
 
 --- Black: Written as `f` in paint files and @{term.blit}, has a default
--- terminal colour of #191919.
+-- terminal colour of #111111.
 black = 0x8000
 
 --- Combines a set of colors (or sets of colors) into a larger set. Useful for
@@ -248,7 +248,6 @@ end
 --- Tests whether `color` is contained within `colors`. Useful for Bundled
 -- Cables.
 --
---
 -- @tparam number colors A color, or color set
 -- @tparam number color A color or set of colors that `colors` should contain.
 -- @treturn boolean If `colors` contains all colors within `color`.
@@ -279,7 +278,7 @@ function packRGB(r, g, b)
     expect(2, g, "number")
     expect(3, b, "number")
     return
-        bit32.band(r * 255, 0xFF) * 2 ^ 16 +
+    bit32.band(r * 255, 0xFF) * 2 ^ 16 +
         bit32.band(g * 255, 0xFF) * 2 ^ 8 +
         bit32.band(b * 255, 0xFF)
 end
@@ -299,9 +298,9 @@ end
 function unpackRGB(rgb)
     expect(1, rgb, "number")
     return
-        bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255,
-        bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255,
-        bit32.band(rgb, 0xFF) / 255
+    bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255,
+    bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255,
+    bit32.band(rgb, 0xFF) / 255
 end
 
 --- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many
@@ -333,3 +332,21 @@ function rgb8(r, g, b)
         return packRGB(r, g, b)
     end
 end
+
+-- Colour to hex lookup table for toBlit
+local color_hex_lookup = {}
+for i = 0, 15 do
+    color_hex_lookup[2 ^ i] = string.format("%x", i)
+end
+
+--- Converts the given color to a paint/blit hex character (0-9a-f).
+--
+-- This is equivalent to converting floor(log_2(color)) to hexadecimal.
+--
+-- @tparam number color The color to convert.
+-- @treturn string The blit hex code of the color.
+function toBlit(color)
+    expect(1, color, "number")
+    return color_hex_lookup[color] or
+        string.format("%x", math.floor(math.log(color) / math.log(2)))
+end
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua
index 1af17ee6c..7ae1006b8 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua
@@ -23,6 +23,25 @@ local function parseLine(tImageArg, sLine)
     table.insert(tImageArg, tLine)
 end
 
+-- Sorts pairs of startX/startY/endX/endY such that the start is always the min
+local function sortCoords(startX, startY, endX, endY)
+    local minX, maxX, minY, maxY
+
+    if startX <= endX then
+        minX, maxX = startX, endX
+    else
+        minX, maxX = endX, startX
+    end
+
+    if startY <= endY then
+        minY, maxY = startY, endY
+    else
+        minY, maxY = endY, startY
+    end
+
+    return minX, maxX, minY, maxY
+end
+
 --- Parses an image from a multi-line string
 --
 -- @tparam string image The string containing the raw-image data.
@@ -71,9 +90,6 @@ function drawPixel(xPos, yPos, colour)
     expect(2, yPos, "number")
     expect(3, colour, "number", "nil")
 
-    if type(xPos) ~= "number" then error("bad argument #1 (expected number, got " .. type(xPos) .. ")", 2) end
-    if type(yPos) ~= "number" then error("bad argument #2 (expected number, got " .. type(yPos) .. ")", 2) end
-    if colour ~= nil and type(colour) ~= "number" then error("bad argument #3 (expected number, got " .. type(colour) .. ")", 2) end
     if colour then
         term.setBackgroundColor(colour)
     end
@@ -111,17 +127,7 @@ function drawLine(startX, startY, endX, endY, colour)
         return
     end
 
-    local minX = math.min(startX, endX)
-    local maxX, minY, maxY
-    if minX == startX then
-        minY = startY
-        maxX = endX
-        maxY = endY
-    else
-        minY = endY
-        maxX = startX
-        maxY = startY
-    end
+    local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
 
     -- TODO: clip to screen rectangle?
 
@@ -177,37 +183,33 @@ function drawBox(startX, startY, endX, endY, nColour)
     endY = math.floor(endY)
 
     if nColour then
-        term.setBackgroundColor(nColour)
+        term.setBackgroundColor(nColour) -- Maintain legacy behaviour
+    else
+        nColour = term.getBackgroundColour()
     end
+    local colourHex = colours.toBlit(nColour)
+
     if startX == endX and startY == endY then
         drawPixelInternal(startX, startY)
         return
     end
 
-    local minX = math.min(startX, endX)
-    local maxX, minY, maxY
-    if minX == startX then
-        minY = startY
-        maxX = endX
-        maxY = endY
-    else
-        minY = endY
-        maxX = startX
-        maxY = startY
-    end
+    local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
+    local width = maxX - minX + 1
 
-    for x = minX, maxX do
-        drawPixelInternal(x, minY)
-        drawPixelInternal(x, maxY)
-    end
-
-    if maxY - minY >= 2 then
-        for y = minY + 1, maxY - 1 do
-            drawPixelInternal(minX, y)
-            drawPixelInternal(maxX, y)
+    for y = minY, maxY do
+        if y == minY or y == maxY then
+            term.setCursorPos(minX, y)
+            term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
+        else
+            term.setCursorPos(minX, y)
+            term.blit(" ", colourHex, colourHex)
+            term.setCursorPos(maxX, y)
+            term.blit(" ", colourHex, colourHex)
         end
     end
 end
+
 --- Draws a filled box on the current term from the specified start position to
 -- the specified end position.
 --
@@ -233,29 +235,23 @@ function drawFilledBox(startX, startY, endX, endY, nColour)
     endY = math.floor(endY)
 
     if nColour then
-        term.setBackgroundColor(nColour)
+        term.setBackgroundColor(nColour) -- Maintain legacy behaviour
+    else
+        nColour = term.getBackgroundColour()
     end
+    local colourHex = colours.toBlit(nColour)
+
     if startX == endX and startY == endY then
         drawPixelInternal(startX, startY)
         return
     end
 
-    local minX = math.min(startX, endX)
-    local maxX, minY, maxY
-    if minX == startX then
-        minY = startY
-        maxX = endX
-        maxY = endY
-    else
-        minY = endY
-        maxX = startX
-        maxY = startY
-    end
+    local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
+    local width = maxX - minX + 1
 
-    for x = minX, maxX do
-        for y = minY, maxY do
-            drawPixelInternal(x, y)
-        end
+    for y = minY, maxY do
+        term.setCursorPos(minX, y)
+        term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
     end
 end
 
diff --git a/src/test/resources/test-rom/spec/apis/colors_spec.lua b/src/test/resources/test-rom/spec/apis/colors_spec.lua
index 86b8adfe9..c7505ca5a 100644
--- a/src/test/resources/test-rom/spec/apis/colors_spec.lua
+++ b/src/test/resources/test-rom/spec/apis/colors_spec.lua
@@ -73,4 +73,20 @@ describe("The colors library", function()
         expect(colors.rgb8(0.3, 0.5, 0.6)):equals(0x4c7f99)
         expect({ colors.rgb8(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
     end)
+
+    describe("colors.toBlit", function()
+        it("validates arguments", function()
+            expect.error(colors.toBlit, nil):eq("bad argument #1 (expected number, got nil)")
+        end)
+
+        it("converts all colors", function()
+            for i = 0, 15 do
+                expect(colors.toBlit(2 ^ i)):eq(string.format("%x", i))
+            end
+        end)
+
+        it("floors colors", function()
+            expect(colors.toBlit(16385)):eq("e")
+        end)
+    end)
 end)
diff --git a/src/test/resources/test-rom/spec/apis/paintutils_spec.lua b/src/test/resources/test-rom/spec/apis/paintutils_spec.lua
index fc72eece2..fc2b6008c 100644
--- a/src/test/resources/test-rom/spec/apis/paintutils_spec.lua
+++ b/src/test/resources/test-rom/spec/apis/paintutils_spec.lua
@@ -1,4 +1,19 @@
+local with_window = require "test_helpers".with_window
+
 describe("The paintutils library", function()
+    -- Verifies that a window's lines are equal to the given table of blit
+    -- strings ({{"text", "fg", "bg"}, {"text", "fg", "bg"}...})
+    local function window_eq(w, state)
+        -- Verification of the size isn't really important in the tests, but
+        -- better safe than sorry.
+        local _, height = w.getSize()
+        expect(#state):eq(height)
+
+        for line = 1, height do
+            expect({ w.getLine(line) }):same(state[line])
+        end
+    end
+
     describe("paintutils.parseImage", function()
         it("validates arguments", function()
             paintutils.parseImage("")
@@ -28,6 +43,30 @@ describe("The paintutils library", function()
             expect.error(paintutils.drawLine, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
             expect.error(paintutils.drawLine, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
         end)
+
+        it("draws a line going across with custom colour", function()
+            local w = with_window(3, 2, function()
+                paintutils.drawLine(1, 1, 3, 1, colours.red)
+            end)
+
+            window_eq(w, {
+                { "   ", "000", "eee" },
+                { "   ", "000", "fff" },
+            })
+        end)
+
+        it("draws a line going diagonally with term colour", function()
+            local w = with_window(3, 3, function()
+                term.setBackgroundColour(colours.red)
+                paintutils.drawLine(1, 1, 3, 3)
+            end)
+
+            window_eq(w, {
+                { "   ", "000", "eff" },
+                { "   ", "000", "fef" },
+                { "   ", "000", "ffe" },
+            })
+        end)
     end)
 
     describe("paintutils.drawBox", function()
@@ -38,6 +77,45 @@ describe("The paintutils library", function()
             expect.error(paintutils.drawBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
             expect.error(paintutils.drawBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
         end)
+
+        it("draws a box with term colour", function()
+            local w = with_window(3, 3, function()
+                term.setBackgroundColour(colours.red)
+                paintutils.drawBox(1, 1, 3, 3)
+            end)
+
+            window_eq(w, {
+                { "   ", "eee", "eee" },
+                { "   ", "e0e", "efe" },
+                { "   ", "eee", "eee" },
+            })
+        end)
+
+        it("draws a box with custom colour", function()
+            local w = with_window(3, 3, function()
+                paintutils.drawBox(1, 1, 3, 3, colours.red)
+            end)
+
+            window_eq(w, {
+                { "   ", "eee", "eee" },
+                { "   ", "e0e", "efe" },
+                { "   ", "eee", "eee" },
+            })
+        end)
+
+        it("draws a box without overwriting existing content", function()
+            local w = with_window(3, 3, function()
+                term.setCursorPos(2, 2)
+                term.write("a")
+                paintutils.drawBox(1, 1, 3, 3, colours.red)
+            end)
+
+            window_eq(w, {
+                { "   ", "eee", "eee" },
+                { " a ", "e0e", "efe" },
+                { "   ", "eee", "eee" },
+            })
+        end)
     end)
 
     describe("paintutils.drawFilledBox", function()
@@ -48,6 +126,31 @@ describe("The paintutils library", function()
             expect.error(paintutils.drawFilledBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
             expect.error(paintutils.drawFilledBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
         end)
+
+        it("draws a filled box with term colour", function()
+            local w = with_window(3, 3, function()
+                term.setBackgroundColour(colours.red)
+                paintutils.drawFilledBox(1, 1, 3, 3)
+            end)
+
+            window_eq(w, {
+                { "   ", "eee", "eee" },
+                { "   ", "eee", "eee" },
+                { "   ", "eee", "eee" },
+            })
+        end)
+
+        it("draws a filled box with custom colour", function()
+            local w = with_window(3, 3, function()
+                paintutils.drawFilledBox(1, 1, 3, 3, colours.red)
+            end)
+
+            window_eq(w, {
+                { "   ", "eee", "eee" },
+                { "   ", "eee", "eee" },
+                { "   ", "eee", "eee" },
+            })
+        end)
     end)
 
     describe("paintutils.drawImage", function()

From d28f42e8b79b3e2b989447acf41e9eefa6b465e3 Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Tue, 2 Mar 2021 18:22:26 -0800
Subject: [PATCH 33/36] use arg[0] in all usage printouts (#571)

---
 patchwork.md                                  |  6 ++
 .../computercraft/lua/rom/programs/alias.lua  |  3 +-
 .../computercraft/lua/rom/programs/cd.lua     |  3 +-
 .../lua/rom/programs/command/exec.lua         |  3 +-
 .../computercraft/lua/rom/programs/copy.lua   |  3 +-
 .../computercraft/lua/rom/programs/delete.lua |  3 +-
 .../computercraft/lua/rom/programs/edit.lua   |  3 +-
 .../computercraft/lua/rom/programs/eject.lua  |  3 +-
 .../lua/rom/programs/fun/advanced/paint.lua   |  3 +-
 .../computercraft/lua/rom/programs/fun/dj.lua |  7 +-
 .../computercraft/lua/rom/programs/gps.lua    |  7 +-
 .../lua/rom/programs/http/pastebin.lua        |  7 +-
 .../lua/rom/programs/http/wget.lua            |  5 +-
 .../computercraft/lua/rom/programs/label.lua  | 14 +--
 .../computercraft/lua/rom/programs/mkdir.lua  |  3 +-
 .../lua/rom/programs/monitor.lua              |  3 +-
 .../computercraft/lua/rom/programs/move.lua   |  3 +-
 .../lua/rom/programs/rednet/chat.lua          |  5 +-
 .../lua/rom/programs/redstone.lua             |  9 +-
 .../computercraft/lua/rom/programs/rename.lua |  3 +-
 .../lua/rom/programs/turtle/craft.lua         |  3 +-
 .../lua/rom/programs/turtle/equip.lua         |  3 +-
 .../lua/rom/programs/turtle/excavate.lua      |  3 +-
 .../lua/rom/programs/turtle/go.lua            |  3 +-
 .../lua/rom/programs/turtle/refuel.lua        |  3 +-
 .../lua/rom/programs/turtle/tunnel.lua        |  3 +-
 .../lua/rom/programs/turtle/turn.lua          |  3 +-
 .../lua/rom/programs/turtle/unequip.lua       |  3 +-
 .../computercraft/lua/rom/programs/type.lua   |  3 +-
 .../spec/programs/command/exec_spec.lua       | 33 +++++++
 .../test-rom/spec/programs/copy_spec.lua      | 40 +++++++++
 .../test-rom/spec/programs/move_spec.lua      | 74 +++++++++++++++
 .../spec/programs/turtle/craft_spec.lua       | 69 ++++++++++++++
 .../spec/programs/turtle/equip_spec.lua       | 89 +++++++++++++++++++
 .../spec/programs/turtle/refuel_spec.lua      | 62 +++++++++++++
 .../spec/programs/turtle/unequip_spec.lua     | 69 ++++++++++++++
 36 files changed, 515 insertions(+), 44 deletions(-)
 create mode 100644 src/test/resources/test-rom/spec/programs/command/exec_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/copy_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/move_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/turtle/craft_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/turtle/equip_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/turtle/refuel_spec.lua
 create mode 100644 src/test/resources/test-rom/spec/programs/turtle/unequip_spec.lua

diff --git a/patchwork.md b/patchwork.md
index 1fb580af8..4e8030962 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -275,3 +275,9 @@ matter? Nah....
 
 Use blit to draw boxes, add colors.toBlit (#570)
 ```
+
+``` 
+d13bd2cce8d102ad7f61f557e707d6fe3731bc37
+
+use arg[0] in all usage printouts (#571)
+```
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/alias.lua b/src/main/resources/data/computercraft/lua/rom/programs/alias.lua
index 79ebedc1f..c3471a723 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/alias.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/alias.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs > 2 then
-    print("Usage: alias  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/cd.lua b/src/main/resources/data/computercraft/lua/rom/programs/cd.lua
index d1a3e86c1..f127ea88b 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/cd.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/cd.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs < 1 then
-    print("Usage: cd ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/command/exec.lua b/src/main/resources/data/computercraft/lua/rom/programs/command/exec.lua
index 67c9446f9..1591b579d 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/command/exec.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/command/exec.lua
@@ -4,7 +4,8 @@ if not commands then
     return
 end
 if #tArgs == 0 then
-    printError("Usage: exec ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    printError("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/copy.lua b/src/main/resources/data/computercraft/lua/rom/programs/copy.lua
index 49d58a0b9..0977d3f8f 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/copy.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/copy.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs < 2 then
-    print("Usage: cp  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/delete.lua b/src/main/resources/data/computercraft/lua/rom/programs/delete.lua
index 620cdd629..1d071619c 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/delete.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/delete.lua
@@ -1,7 +1,8 @@
 local args = table.pack(...)
 
 if args.n < 1 then
-    print("Usage: rm ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua
index 007f37a24..8f0af9356 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua
@@ -1,7 +1,8 @@
 -- Get file to edit
 local tArgs = { ... }
 if #tArgs == 0 then
-    print("Usage: edit ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/eject.lua b/src/main/resources/data/computercraft/lua/rom/programs/eject.lua
index b15e2f20f..3deb9c2a2 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/eject.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/eject.lua
@@ -1,7 +1,8 @@
 -- Get arguments
 local tArgs = { ... }
 if #tArgs == 0 then
-    print("Usage: eject ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua
index b7c44526a..cb9de4153 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua
@@ -34,7 +34,8 @@ end
 -- Determines if the file exists, and can be edited on this computer
 local tArgs = { ... }
 if #tArgs == 0 then
-    print("Usage: paint ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 local sPath = shell.resolve(tArgs[1])
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/fun/dj.lua b/src/main/resources/data/computercraft/lua/rom/programs/fun/dj.lua
index 322b4a887..48b4aa06c 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/fun/dj.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/fun/dj.lua
@@ -1,10 +1,11 @@
 local tArgs = { ... }
 
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("dj play")
-    print("dj play ")
-    print("dj stop")
+    print(programName .. " play")
+    print(programName .. " play ")
+    print(programName .. " stop")
 end
 
 if #tArgs > 2 then
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/gps.lua b/src/main/resources/data/computercraft/lua/rom/programs/gps.lua
index c0194443b..3e0590e9b 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/gps.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/gps.lua
@@ -1,8 +1,9 @@
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("gps host")
-    print("gps host   ")
-    print("gps locate")
+    print(programName .. " host")
+    print(programName .. " host   ")
+    print(programName .. " locate")
 end
 
 local tArgs = { ... }
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/http/pastebin.lua b/src/main/resources/data/computercraft/lua/rom/programs/http/pastebin.lua
index 15ad89f36..807ef603f 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/http/pastebin.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/http/pastebin.lua
@@ -1,8 +1,9 @@
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("pastebin put ")
-    print("pastebin get  ")
-    print("pastebin run  ")
+    print(programName .. " put ")
+    print(programName .. " get  ")
+    print(programName .. " run  ")
 end
 
 local tArgs = { ... }
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua b/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua
index 7b5c7654e..ac5fc4fc0 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua
@@ -1,7 +1,8 @@
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usage:")
-    print("wget  [filename]")
-    print("wget run ")
+    print(programName .. "  [filename]")
+    print(programName .. " run ")
 end
 
 local tArgs = { ... }
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/label.lua b/src/main/resources/data/computercraft/lua/rom/programs/label.lua
index f857dac5c..8e413e09a 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/label.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/label.lua
@@ -1,13 +1,15 @@
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("label get")
-    print("label get ")
-    print("label set ")
-    print("label set  ")
-    print("label clear")
-    print("label clear ")
+    print(programName .. " get")
+    print(programName .. " get ")
+    print(programName .. " set ")
+    print(programName .. " set  ")
+    print(programName .. " clear")
+    print(programName .. " clear ")
 end
 
+
 local function checkDrive(sDrive)
     if peripheral.getType(sDrive) == "drive" then
         -- Check the disk exists
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/mkdir.lua b/src/main/resources/data/computercraft/lua/rom/programs/mkdir.lua
index bbdd08002..4e1b8ac10 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/mkdir.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/mkdir.lua
@@ -1,7 +1,8 @@
 local tArgs = { ... }
 
 if #tArgs < 1 then
-    print("Usage: mkdir ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua b/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua
index 01c9d7949..e97558357 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua
@@ -1,5 +1,6 @@
 local function printUsage()
-    print("Usage: monitor   ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "   ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/move.lua b/src/main/resources/data/computercraft/lua/rom/programs/move.lua
index 254592200..3f68236d0 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/move.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/move.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs < 2 then
-    print("Usage: mv  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/rednet/chat.lua b/src/main/resources/data/computercraft/lua/rom/programs/rednet/chat.lua
index 1bf5582b2..6585d0675 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/rednet/chat.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/rednet/chat.lua
@@ -1,9 +1,10 @@
 local tArgs = { ... }
 
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("chat host ")
-    print("chat join  ")
+    print(programName .. " host ")
+    print(programName .. " join  ")
 end
 
 local sOpenedModem = nil
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/redstone.lua b/src/main/resources/data/computercraft/lua/rom/programs/redstone.lua
index 65d6c5e1f..7c3165c1c 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/redstone.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/redstone.lua
@@ -1,11 +1,12 @@
 local tArgs = { ... }
 
 local function printUsage()
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
     print("Usages:")
-    print("redstone probe")
-    print("redstone set  ")
-    print("redstone set   ")
-    print("redstone pulse   ")
+    print(programName .. " probe")
+    print(programName .. " set  ")
+    print(programName .. " set   ")
+    print(programName .. " pulse   ")
 end
 
 local sCommand = tArgs[1]
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/rename.lua b/src/main/resources/data/computercraft/lua/rom/programs/rename.lua
index 8b491abcd..e627bb27f 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/rename.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/rename.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs < 2 then
-    print("Usage: rename  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua
index 65f93104b..34da46aea 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua
@@ -1,5 +1,6 @@
 if not turtle then
-    printError("Requires a Turtle")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " [number]")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/equip.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/equip.lua
index b69ef4c45..9f0d60735 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/equip.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/equip.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 local function printUsage()
-    print("Usage: equip  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
 end
 
 if #tArgs ~= 2 then
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/excavate.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/excavate.lua
index 9d4313eb1..1bb0488fe 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/excavate.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/excavate.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 if #tArgs ~= 1 then
-    print("Usage: excavate ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/go.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/go.lua
index 9c6de67ce..5b6c658e2 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/go.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/go.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 if #tArgs < 1 then
-    print("Usage: go  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/refuel.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/refuel.lua
index d9dd5b002..138cb9e47 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/refuel.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/refuel.lua
@@ -6,7 +6,8 @@ end
 local tArgs = { ... }
 local nLimit = 1
 if #tArgs > 1 then
-    print("Usage: refuel [number]")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " [number]")
     return
 elseif #tArgs > 0 then
     if tArgs[1] == "all" then
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/tunnel.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/tunnel.lua
index 34c0807d9..821965390 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/tunnel.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/tunnel.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 if #tArgs ~= 1 then
-    print("Usage: tunnel ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/turn.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/turn.lua
index e3fbbcbd3..c4d6ceeb7 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/turn.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/turn.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 if #tArgs < 1 then
-    print("Usage: turn  ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. "  ")
     return
 end
 
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/unequip.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/unequip.lua
index 97501aa42..82a37c895 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/unequip.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/unequip.lua
@@ -5,7 +5,8 @@ end
 
 local tArgs = { ... }
 local function printUsage()
-    print("Usage: unequip ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
 end
 
 if #tArgs ~= 1 then
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/type.lua b/src/main/resources/data/computercraft/lua/rom/programs/type.lua
index ccbaf0dd8..aa2ee54bb 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/type.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/type.lua
@@ -1,6 +1,7 @@
 local tArgs = { ... }
 if #tArgs < 1 then
-    print("Usage: type ")
+    local programName = arg[0] or fs.getName(shell.getRunningProgram())
+    print("Usage: " .. programName .. " ")
     return
 end
 
diff --git a/src/test/resources/test-rom/spec/programs/command/exec_spec.lua b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua
new file mode 100644
index 000000000..4e6b37b2c
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua
@@ -0,0 +1,33 @@
+local capture = require "test_helpers".capture_program
+
+describe("The exec program", function()
+    it("displays an error without the commands api", function()
+        stub(_G, "commands", nil)
+        expect(capture(stub, "/rom/programs/command/exec.lua"))
+        :matches { ok = true, output = "", error = "Requires a Command Computer.\n" }
+    end)
+
+    it("displays its usage when given no argument", function()
+        stub(_G, "commands", {})
+        expect(capture(stub, "/rom/programs/command/exec.lua"))
+        :matches { ok = true, output = "", error = "Usage: /rom/programs/command/exec.lua \n" }
+    end)
+
+    it("runs a command", function()
+        stub(_G, "commands", {
+            exec = function() return true, { "Hello World!" } end,
+        })
+
+        expect(capture(stub, "/rom/programs/command/exec.lua computercraft"))
+        :matches { ok = true, output = "Success\nHello World!\n", error = "" }
+    end)
+
+    it("reports command failures", function()
+        stub(_G, "commands", {
+            exec = function() return false, { "Hello World!" } end,
+        })
+
+        expect(capture(stub, "/rom/programs/command/exec.lua computercraft"))
+        :matches { ok = true, output = "Hello World!\n", error = "Failed\n" }
+    end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/copy_spec.lua b/src/test/resources/test-rom/spec/programs/copy_spec.lua
new file mode 100644
index 000000000..c18dd54c6
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/copy_spec.lua
@@ -0,0 +1,40 @@
+local capture = require "test_helpers".capture_program
+
+describe("The copy program", function()
+    local function touch(file)
+        io.open(file, "w"):close()
+    end
+
+    it("copies a file", function()
+        touch("/test-files/copy/a.txt")
+
+        shell.run("copy /test-files/copy/a.txt /test-files/copy/b.txt")
+
+        expect(fs.exists("/test-files/copy/a.txt")):eq(true)
+        expect(fs.exists("/test-files/copy/b.txt")):eq(true)
+    end)
+
+    it("fails when copying a non-existent file", function()
+        expect(capture(stub, "copy nothing destination"))
+        :matches { ok = true, output = "", error = "No matching files\n" }
+    end)
+
+    it("fails when overwriting an existing file", function()
+        touch("/test-files/copy/c.txt")
+
+        expect(capture(stub, "copy /test-files/copy/c.txt /test-files/copy/c.txt"))
+        :matches { ok = true, output = "", error = "Destination exists\n" }
+    end)
+
+    it("fails when copying into read-only locations", function()
+        touch("/test-files/copy/d.txt")
+
+        expect(capture(stub, "copy /test-files/copy/d.txt /rom/test.txt"))
+        :matches { ok = true, output = "", error = "Destination is read-only\n" }
+    end)
+
+    it("displays the usage when given no arguments", function()
+        expect(capture(stub, "copy"))
+        :matches { ok = true, output = "Usage: copy  \n", error = "" }
+    end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/move_spec.lua b/src/test/resources/test-rom/spec/programs/move_spec.lua
new file mode 100644
index 000000000..7fa8a3857
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/move_spec.lua
@@ -0,0 +1,74 @@
+local capture = require "test_helpers".capture_program
+
+describe("The move program", function()
+    local function cleanup() fs.delete("/test-files/move") end
+    local function touch(file)
+        io.open(file, "w"):close()
+    end
+
+    it("move a file", function()
+        cleanup()
+        touch("/test-files/move/a.txt")
+
+        shell.run("move /test-files/move/a.txt /test-files/move/b.txt")
+
+        expect(fs.exists("/test-files/move/a.txt")):eq(false)
+        expect(fs.exists("/test-files/move/b.txt")):eq(true)
+    end)
+
+    it("moves a file to a directory", function()
+        cleanup()
+        touch("/test-files/move/a.txt")
+        fs.makeDir("/test-files/move/a")
+
+        expect(capture(stub, "move /test-files/move/a.txt /test-files/move/a"))
+        :matches { ok = true }
+
+        expect(fs.exists("/test-files/move/a.txt")):eq(false)
+        expect(fs.exists("/test-files/move/a/a.txt")):eq(true)
+    end)
+
+    it("fails when moving a file which doesn't exist", function()
+        expect(capture(stub, "move nothing destination"))
+        :matches { ok = true, output = "", error = "No matching files\n" }
+    end)
+
+    it("fails when overwriting an existing file", function()
+        cleanup()
+        touch("/test-files/move/a.txt")
+
+        expect(capture(stub, "move /test-files/move/a.txt /test-files/move/a.txt"))
+        :matches { ok = true, output = "", error = "Destination exists\n" }
+    end)
+
+    it("fails when moving to read-only locations", function()
+        cleanup()
+        touch("/test-files/move/a.txt")
+
+        expect(capture(stub, "move /test-files/move/a.txt /rom/test.txt"))
+        :matches { ok = true, output = "", error = "Destination is read-only\n" }
+    end)
+
+    it("fails when moving from read-only locations", function()
+        expect(capture(stub, "move /rom/startup.lua /test-files/move/not-exist.txt"))
+        :matches { ok = true, output = "", error = "Cannot move read-only file /rom/startup.lua\n" }
+    end)
+
+    it("fails when moving mounts", function()
+        expect(capture(stub, "move /rom /test-files/move/rom"))
+        :matches { ok = true, output = "", error = "Cannot move mount /rom\n" }
+    end)
+
+    it("fails when moving a file multiple times", function()
+        cleanup()
+        touch("/test-files/move/a.txt")
+        touch("/test-files/move/b.txt")
+        expect(capture(stub, "move /test-files/move/*.txt /test-files/move/c.txt"))
+        :matches { ok = true, output = "", error = "Cannot overwrite file multiple times\n" }
+    end)
+
+    it("displays the usage with no arguments", function()
+        expect(capture(stub, "move"))
+        :matches { ok = true, output = "Usage: move  \n", error = "" }
+    end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/turtle/craft_spec.lua b/src/test/resources/test-rom/spec/programs/turtle/craft_spec.lua
new file mode 100644
index 000000000..95b953b38
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/turtle/craft_spec.lua
@@ -0,0 +1,69 @@
+local capture = require "test_helpers".capture_program
+
+describe("The craft program", function()
+    it("errors when not a turtle", function()
+        stub(_G, "turtle", nil)
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua"))
+        :matches { ok = true, output = "", error = "Requires a Turtle\n" }
+    end)
+
+    it("fails when turtle.craft() is unavailable", function()
+        stub(_G, "turtle", {})
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua"))
+        :matches { ok = true, output = "Requires a Crafty Turtle\n", error = "" }
+    end)
+
+    it("displays its usage when given no arguments", function()
+        stub(_G, "turtle", { craft = function() end })
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua"))
+        :matches { ok = true, output = "Usage: /rom/programs/turtle/craft.lua [number]\n", error = "" }
+    end)
+
+    it("crafts multiple items", function()
+        local item_count = 3
+        stub(_G, "turtle", {
+            craft = function()
+                item_count = 1
+                return true
+            end,
+            getItemCount = function() return item_count end,
+            getSelectedSlot = function() return 1 end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua 2"))
+        :matches { ok = true, output = "2 items crafted\n", error = "" }
+    end)
+
+    it("craft a single item", function()
+        local item_count = 2
+        stub(_G, "turtle", {
+            craft = function()
+                item_count = 1
+                return true
+            end,
+            getItemCount = function() return item_count end,
+            getSelectedSlot = function() return 1 end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua 1"))
+        :matches { ok = true, output = "1 item crafted\n", error = "" }
+    end)
+
+    it("crafts no items", function()
+        local item_count = 2
+        stub(_G, "turtle", {
+            craft = function()
+                item_count = 1
+                return false
+            end,
+            getItemCount = function() return item_count end,
+            getSelectedSlot = function() return 1 end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/craft.lua 1"))
+        :matches { ok = true, output = "No items crafted\n", error = "" }
+    end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/turtle/equip_spec.lua b/src/test/resources/test-rom/spec/programs/turtle/equip_spec.lua
new file mode 100644
index 000000000..cc12aa367
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/turtle/equip_spec.lua
@@ -0,0 +1,89 @@
+local capture = require "test_helpers".capture_program
+
+describe("The turtle equip program", function()
+    it("errors when not a turtle", function()
+        stub(_G, "turtle", nil)
+
+        expect(capture(stub, "/rom/programs/turtle/equip.lua"))
+        :matches { ok = true, output = "", error = "Requires a Turtle\n" }
+    end)
+
+
+    it("displays its usage when given no arguments", function()
+        stub(_G, "turtle", {})
+
+        expect(capture(stub, "/rom/programs/turtle/equip.lua"))
+        :matches { ok = true, output = "Usage: /rom/programs/turtle/equip.lua  \n", error = "" }
+    end)
+
+    it("equip nothing", function()
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return 0 end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 left"))
+        :matches { ok = true, output = "Nothing to equip\n", error = "" }
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 right"))
+        :matches { ok = true, output = "Nothing to equip\n", error = "" }
+    end)
+
+    it("swaps existing upgrades", function()
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return 1 end,
+            equipLeft = function() return true end,
+            equipRight = function() return true end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 left"))
+        :matches { ok = true, output = "Items swapped\n", error = "" }
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 right"))
+        :matches { ok = true, output = "Items swapped\n", error = "" }
+    end)
+
+    describe("equips a new upgrade", function()
+        local function setup()
+            local item_count = 1
+            stub(_G, "turtle", {
+                select = function() end,
+                getItemCount = function() return item_count end,
+                equipLeft = function()
+                    item_count  = 0
+                    return true
+                end,
+                equipRight = function()
+                    item_count = 0
+                    return true
+                end,
+            })
+        end
+
+        it("on the left", function()
+            setup()
+            expect(capture(stub, "/rom/programs/turtle/equip.lua 1 left"))
+            :matches { ok = true, output = "Item equipped\n", error = "" }
+        end)
+
+        it("on the right", function()
+            setup()
+            expect(capture(stub, "/rom/programs/turtle/equip.lua 1 right"))
+            :matches { ok = true, output = "Item equipped\n", error = "" }
+        end)
+    end)
+
+    it("handles when an upgrade cannot be equipped", function()
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return 1 end,
+            equipLeft = function() return false end,
+            equipRight = function() return false end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 left"))
+        :matches { ok = true, output = "Item not equippable\n", error = "" }
+        expect(capture(stub, "/rom/programs/turtle/equip.lua 1 right"))
+        :matches { ok = true, output = "Item not equippable\n", error = "" }
+    end)
+
+end)
diff --git a/src/test/resources/test-rom/spec/programs/turtle/refuel_spec.lua b/src/test/resources/test-rom/spec/programs/turtle/refuel_spec.lua
new file mode 100644
index 000000000..5ba397b72
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/turtle/refuel_spec.lua
@@ -0,0 +1,62 @@
+local capture = require "test_helpers".capture_program
+
+describe("The refuel program", function()
+    local function setup_turtle(fuel_level, fuel_limit, item_count)
+        stub(_G, "turtle", {
+            getFuelLevel = function()
+                return fuel_level
+            end,
+            getItemCount = function()
+                return item_count
+            end,
+            refuel = function(nLimit)
+                item_count = item_count - nLimit
+                fuel_level = fuel_level + nLimit
+            end,
+            select = function()
+            end,
+            getFuelLimit = function()
+                return fuel_limit
+            end,
+        })
+    end
+
+    it("errors when not a turtle", function()
+        stub(_G, "turtle", nil)
+
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua"))
+        :matches { ok = true, output = "", error = "Requires a Turtle\n" }
+    end)
+
+
+    it("displays its usage when given too many argument", function()
+        setup_turtle(0, 5, 0)
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua a b"))
+        :matches { ok = true, output = "Usage: /rom/programs/turtle/refuel.lua [number]\n", error = "" }
+    end)
+
+    it("requires a numeric argument", function()
+        setup_turtle(0, 0, 0)
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua nothing"))
+        :matches { ok = true, output = "Invalid limit, expected a number or \"all\"\n", error = "" }
+    end)
+
+    it("refuels the turtle", function()
+        setup_turtle(0, 10, 5)
+
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua 5"))
+        :matches { ok = true, output = "Fuel level is 5\n", error = "" }
+    end)
+
+    it("reports when the fuel limit is reached", function()
+        setup_turtle(0, 5, 5)
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua 5"))
+        :matches { ok = true, output = "Fuel level is 5\nFuel limit reached\n", error = "" }
+    end)
+
+    it("reports when the fuel level is unlimited", function()
+        setup_turtle("unlimited", 5, 5)
+        expect(capture(stub, "/rom/programs/turtle/refuel.lua 5"))
+        :matches { ok = true, output = "Fuel level is unlimited\n", error = "" }
+    end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/turtle/unequip_spec.lua b/src/test/resources/test-rom/spec/programs/turtle/unequip_spec.lua
new file mode 100644
index 000000000..627d5952a
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/turtle/unequip_spec.lua
@@ -0,0 +1,69 @@
+local capture = require "test_helpers".capture_program
+
+describe("The turtle unequip program", function()
+    it("errors when not a turtle", function()
+        stub(_G, "turtle", nil)
+
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua"))
+        :matches { ok = true, output = "", error = "Requires a Turtle\n" }
+    end)
+
+
+    it("displays its usage when given no arguments", function()
+        stub(_G, "turtle", {})
+
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua"))
+        :matches { ok = true, output = "Usage: /rom/programs/turtle/unequip.lua \n", error = "" }
+    end)
+
+    it("says when nothing was unequipped", function()
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return 0 end,
+            equipRight = function() return true end,
+            equipLeft = function() return true end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua left"))
+        :matches { ok = true, output = "Nothing to unequip\n", error = "" }
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua right"))
+        :matches { ok = true, output = "Nothing to unequip\n", error = "" }
+    end)
+
+    it("unequips a upgrade", function()
+        local item_count = 0
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return item_count end,
+            equipRight = function()
+                item_count = 1
+                return true
+            end,
+            equipLeft = function()
+                item_count = 1
+                return true
+            end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua left"))
+        :matches { ok = true, output = "Item unequipped\n", error = "" }
+        item_count = 0
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua right"))
+        :matches { ok = true, output = "Item unequipped\n", error = "" }
+    end)
+
+    it("fails when the turtle is full", function()
+        stub(_G, "turtle", {
+            select = function() end,
+            getItemCount = function() return 1 end,
+            equipRight = function() return true end,
+            equipLeft = function() return true end,
+        })
+
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua left"))
+        :matches { ok = true, output = "No space to unequip item\n", error = "" }
+        expect(capture(stub, "/rom/programs/turtle/unequip.lua right"))
+        :matches { ok = true, output = "No space to unequip item\n", error = "" }
+    end)
+
+end)

From 43408bf085b1e85dcf65253302c26d9255b5526f Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Tue, 2 Mar 2021 18:27:17 -0800
Subject: [PATCH 34/36] CC:R 1.94.0

---
 README.md         | 2 +-
 gradle.properties | 2 +-
 patchwork.md      | 6 ++++++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index cffe6d32f..ed957950c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 # This is a Work In Progress Port
 *it runs and works-ish*
 
-## Reached Parity with CC:T 1.93.0
+## Reached Parity with CC:T 1.94.0
 
 THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T
 
diff --git a/gradle.properties b/gradle.properties
index e39d02601..31c68add1 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,7 +2,7 @@
 org.gradle.jvmargs=-Xmx1G
 
 # Mod properties
-mod_version=1.93.1
+mod_version=1.94.0
 
 # Minecraft properties
 mc_version=1.16.2
diff --git a/patchwork.md b/patchwork.md
index 4e8030962..be1d405c9 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -281,3 +281,9 @@ d13bd2cce8d102ad7f61f557e707d6fe3731bc37
 
 use arg[0] in all usage printouts (#571)
 ```
+
+```
+74ac5bb3d17e5bee30643a5d6702696600c06229
+
+Bump to 1.94.0
+```

From e0fc9948192ca953c96ba2ecd0251bcc5edd1708 Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Tue, 2 Mar 2021 22:47:44 -0800
Subject: [PATCH 35/36] [TODO] Auto-generate monitor models

I didn't think it was worth it, and then I found myself needing to
update a dozen of them. The code isn't especially pretty, but it works,
so that's fine.

Also fixes several issues with us using the wrong texture (closes #572).
I've put together a wiki page[1] which describes each texture in a
little more detail.

[1] https://github.com/SquidDev-CC/CC-Tweaked/wiki/Monitor-texture-reference
---
 patchwork.md | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/patchwork.md b/patchwork.md
index be1d405c9..f1d77676b 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -287,3 +287,21 @@ use arg[0] in all usage printouts (#571)
 
 Bump to 1.94.0
 ```
+
+[TODO] [M3R1-02] Zero Clue how to reimplement this in fabric.
+``` 
+c8aeddedd4ed430f9cb6428676ebb4fa39834182
+
+Auto-generate monitor models
+
+I didn't think it was worth it, and then I found myself needing to
+update a dozen of them. The code isn't especially pretty, but it works,
+so that's fine.
+
+Also fixes several issues with us using the wrong texture (closes #572).
+I've put together a wiki page[1] which describes each texture in a
+little more detail.
+
+[1] https://github.com/SquidDev-CC/CC-Tweaked/wiki/Monitor-texture-reference
+```
+

From 31a10274018aec1b4993bc96fcef4b7621ca3b85 Mon Sep 17 00:00:00 2001
From: Merith-TK 
Date: Tue, 2 Mar 2021 23:12:53 -0800
Subject: [PATCH 36/36] Clean up some examples a little bit

  Would be good if they didn't crash and burn on entry :).
---
 patchwork.md                                         |  6 ++++++
 .../data/computercraft/lua/rom/apis/colors.lua       |  8 ++++----
 .../data/computercraft/lua/rom/apis/disk.lua         |  2 +-
 .../data/computercraft/lua/rom/apis/help.lua         |  2 +-
 .../data/computercraft/lua/rom/apis/keys.lua         |  1 +
 .../data/computercraft/lua/rom/apis/peripheral.lua   |  2 +-
 .../data/computercraft/lua/rom/apis/textutils.lua    | 12 ++++++------
 .../lua/rom/modules/main/cc/completion.lua           |  4 ++--
 .../computercraft/lua/rom/modules/main/cc/pretty.lua |  4 ++--
 .../lua/rom/modules/main/cc/shell/completion.lua     |  2 +-
 .../data/computercraft/lua/rom/programs/shell.lua    |  4 ++--
 11 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/patchwork.md b/patchwork.md
index f1d77676b..1e4b2208d 100644
--- a/patchwork.md
+++ b/patchwork.md
@@ -305,3 +305,9 @@ little more detail.
 [1] https://github.com/SquidDev-CC/CC-Tweaked/wiki/Monitor-texture-reference
 ```
 
+7f90f2f7cadce0d5b9177b16626979591bce8137
+``` 
+Clean up some examples a little bit
+
+Would be good if they didn't crash and burn on entry :).
+```
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
index 2a7ca763e..1add04d6a 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua
@@ -270,7 +270,7 @@ end
 -- @treturn number The combined hexadecimal colour.
 -- @usage
 -- ```lua
--- colors.rgb(0.7, 0.2, 0.6)
+-- colors.unpackRGB(0.7, 0.2, 0.6)
 -- -- => 0xb23399
 -- ```
 function packRGB(r, g, b)
@@ -291,7 +291,7 @@ end
 -- @treturn number The blue channel, will be between 0 and 1.
 -- @usage
 -- ```lua
--- colors.rgb(0xb23399)
+-- colors.unpackRGB(0xb23399)
 -- -- => 0.7, 0.2, 0.6
 -- ```
 -- @see colors.packRGB
@@ -317,12 +317,12 @@ end
 -- @deprecated Use @{packRGB} or @{unpackRGB} directly.
 -- @usage
 -- ```lua
--- colors.rgb(0xb23399)
+-- colors.unpackRGB(0xb23399)
 -- -- => 0.7, 0.2, 0.6
 -- ```
 -- @usage
 -- ```lua
--- colors.rgb(0.7, 0.2, 0.6)
+-- colors.unpackRGB(0.7, 0.2, 0.6)
 -- -- => 0xb23399
 -- ```
 function rgb8(r, g, b)
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua
index 9e136b22c..ca5ac21d2 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua
@@ -22,7 +22,7 @@ end
 --
 -- @tparam string name The name of the disk drive.
 -- @treturn boolean If something is in the disk drive.
--- @usage disk.isPresent(false)
+-- @usage disk.isPresent("top")
 function isPresent(name)
     if isDrive(name) then
         return peripheral.call(name, "isDiskPresent")
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/help.lua b/src/main/resources/data/computercraft/lua/rom/apis/help.lua
index 438af474f..43b3d6b17 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/help.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/help.lua
@@ -32,7 +32,7 @@ end
 -- @tparam string topic The topic to find
 -- @treturn string|nil The path to the given topic's help file, or `nil` if it
 -- cannot be found.
--- @usage print(help.lookup("disk"))
+-- @usage help.lookup("disk")
 function lookup(_sTopic)
     expect(1, _sTopic, "string")
     -- Look on the path variable
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/keys.lua b/src/main/resources/data/computercraft/lua/rom/apis/keys.lua
index 90f877aa5..206646c21 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/keys.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/keys.lua
@@ -145,6 +145,7 @@ keys.cimcumflex = keys.circumflex --- @local
 --
 -- @tparam number code The key code to look up.
 -- @treturn string|nil The name of the key, or `nil` if not a valid key code.
+-- @usage keys.getName(keys.enter)
 function getName(_nKey)
     expect(1, _nKey, "number")
     return tKeys[_nKey]
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua
index d8be09ad3..c284d0691 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua
@@ -191,7 +191,7 @@ end
 -- filter function, which takes the peripheral's name and wrapped table
 -- and returns if it should be included in the result.
 -- @treturn table... 0 or more wrapped peripherals matching the given filters.
--- @usage local monitors = { peripheral.find("monitor") }
+-- @usage { peripheral.find("monitor") }
 -- @usage peripheral.find("modem", rednet.open)
 function find(ty, filter)
     expect(1, ty, "string")
diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
index 358557b55..a55991fc9 100644
--- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
+++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua
@@ -9,7 +9,7 @@ local expect, field = expect.expect, expect.field
 --- Slowly writes string text at current cursor position,
 -- character-by-character.
 --
--- Like @{write}, this does not insert a newline at the end.
+-- Like @{_G.write}, this does not insert a newline at the end.
 --
 -- @tparam string sText The the text to write to the screen
 -- @tparam[opt] number nRate The number of characters to write each second,
@@ -119,8 +119,8 @@ end
 -- displayed before prompting.
 -- @treturn number The number of lines printed.
 -- @usage
--- local width, height = term.getSize()
--- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2)
+--     local width, height = term.getSize()
+--     textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2)
 function pagedPrint(_sText, _nFreeLines)
     expect(2, _nFreeLines, "number", "nil")
     -- Setup a redirector
@@ -706,7 +706,7 @@ unserialiseJSON = unserialise_json
 --
 -- @tparam string str The string to encode
 -- @treturn string The encoded string.
--- @usage print("https://example.com/?view=" .. textutils.urlEncode(read()))
+-- @usage print("https://example.com/?view=" .. textutils.urlEncode("some text&things"))
 function urlEncode(str)
     expect(1, str, "string")
     if str then
@@ -744,8 +744,8 @@ local tEmpty = {}
 --
 -- @treturn { string... } The (possibly empty) list of completions.
 -- @see shell.setCompletionFunction
--- @see read
--- @usage textutils.complete( "pa", getfenv() )
+-- @see _G.read
+-- @usage textutils.complete( "pa", _ENV )
 function complete(sSearchText, tSearchTable)
     expect(1, sSearchText, "string")
     expect(2, tSearchTable, "table", "nil")
diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua
index 2ae2e4966..01cc187b1 100644
--- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua
+++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua
@@ -1,5 +1,5 @@
 --- A collection of helper methods for working with input completion, such
--- as that require by @{read}.
+-- as that require by @{_G.read}.
 --
 -- @module cc.completion
 -- @see cc.shell.completion For additional helpers to use with
@@ -29,7 +29,7 @@ end
 -- @tparam { string... } choices The list of choices to complete from.
 -- @tparam[opt] boolean add_space Whether to add a space after the completed item.
 -- @treturn { string... } A list of suffixes of matching strings.
--- @usage Call @{read}, completing the names of various animals.
+-- @usage Call @{_G.read}, completing the names of various animals.
 --
 --     local animals = { "dog", "cat", "lion", "unicorn" }
 --     read(nil, nil, function(text) return choice(text, animals) end)
diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua
index a62a6f9f1..1c782ecee 100644
--- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua
+++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua
@@ -13,11 +13,11 @@
 -- @module cc.pretty
 -- @usage Print a table to the terminal
 --     local pretty = require "cc.pretty"
---     pretty.write(pretty.pretty({ 1, 2, 3 }))
+--     pretty.print(pretty.pretty({ 1, 2, 3 }))
 --
 -- @usage Build a custom document and display it
 --     local pretty = require "cc.pretty"
---     pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world")))
+--     pretty.print(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world")))
 
 local expect = require "cc.expect"
 local expect, field = expect.expect, expect.field
diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua
index 91d9ea7a4..21c10ec34 100644
--- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua
+++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua
@@ -8,7 +8,7 @@
 -- wrap them using @{build}, or your own custom function.
 --
 -- @module cc.shell.completion
--- @see cc.completion For more general helpers, suitable for use with @{read}.
+-- @see cc.completion For more general helpers, suitable for use with @{_G.read}.
 -- @see shell.setCompletionFunction
 
 local expect = require "cc.expect".expect
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
index 0c6b5868b..1cc6dfe48 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
@@ -383,7 +383,7 @@ end
 --
 -- @tparam string sLine The input to complete.
 -- @treturn { string }|nil The list of possible completions.
--- @see read For more information about completion.
+-- @see _G.read For more information about completion.
 -- @see shell.completeProgram
 -- @see shell.setCompletionFunction
 -- @see shell.getCompletionInfo
@@ -461,7 +461,7 @@ end
 -- The completion function.
 -- @see cc.shell.completion Various utilities to help with writing completion functions.
 -- @see shell.complete
--- @see read For more information about completion.
+-- @see _G.read For more information about completion.
 function shell.setCompletionFunction(program, complete)
     expect(1, program, "string")
     expect(2, complete, "function")