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/.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/.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 65b2c3ce2..ed957950c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# 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* + +## 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 + +The changelog is located at [PatchWork.md](patchwork.md) diff --git a/gradle.properties b/gradle.properties index d728c1e8f..31c68add1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Mod properties -mod_version=1.91.2 +mod_version=1.94.0 # Minecraft properties mc_version=1.16.2 diff --git a/patchwork.md b/patchwork.md new file mode 100644 index 000000000..1e4b2208d --- /dev/null +++ b/patchwork.md @@ -0,0 +1,313 @@ +# Just my list of things I have ported over + +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 +``` + +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 +5155e18de279a193c558aa029963486fd1294769 +Added translation for Vietnamese +Co-authored-by: Boom +``` + +``` +7e121ff72f2b1504cd6af47b57500876682bac45 +ae6124d1f477487abab1858abde8c4ec49dfee3c +Translations for Vienamese +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. +``` + +``` +87393e8aef9ddfaca465d626ee7cff5ff499a7e8 +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 +``` + +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. +``` + +``` +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 +``` + +``` +0832974725b2478c5227b81f82c35bbf03cf6aba +Translations for Swedish + +Co-authored-by: David Isaksson +``` + +``` +84036d97d99efd8762e0170002060ae3471508bf +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. +``` + +``` +30d35883b83831900b34040f0131c7e06f5c3e52 +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) +``` + +All API Documentation updates, +`Not Needed` for this repo. +``` +93068402a2ffec00eedb8fe2d859ebdc005a1989 +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. +``` + +``` +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. +``` + +``` +c334423d42ba3b653ac3a8c27bce7970457f8f96 +Add function to get window visibility + +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 +``` + +``` +Not Needed +6aae4e576621090840724e094aa25e51696530fc +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 +``` + +``` +17a932920711a5c0361a5048c9e0a5e7a58e6364 +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. +``` + +``` +c58441b29c3715f092e7f3747bb3ec65ae5a3d29 +Various SNBT parsing improvements + +Correctly handle: + - Typed arrays ([I; 1, 2, 3]) + - All suffixed numbers (1.2d) + - Single-quoted strings + +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.... +``` + +``` +741adfa7bb2b950d2851c3f0072d6a4769f22773 + +Use blit to draw boxes, add colors.toBlit (#570) +``` + +``` +d13bd2cce8d102ad7f61f557e707d6fe3731bc37 + +use arg[0] in all usage printouts (#571) +``` + +``` +74ac5bb3d17e5bee30643a5d6702696600c06229 + +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 +``` + +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/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/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/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; 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/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() { 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
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/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) {
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 23e86ad76..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,22 +43,41 @@ 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;
     }
 
     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();
@@ -71,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);
         }
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) {
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"
 }
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..b4261b8cb
--- /dev/null
+++ b/src/main/resources/assets/computercraft/lang/vi.json
@@ -0,0 +1,48 @@
+{
+    "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"
+}
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);
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..1add04d6a 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 @@ -74,10 +196,11 @@ 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. +--- 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,8 @@ 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. @@ -145,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) @@ -153,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 @@ -166,16 +291,16 @@ 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 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 @@ -192,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) @@ -207,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/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/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. 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/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/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 40de6924b..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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -528,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 @@ -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 @@ -699,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 @@ -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) @@ -737,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/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index d93533f03..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) @@ -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}. @@ -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/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 b934aa995..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.dump({ 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/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/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/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/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") 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/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 ) ); + } +} 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() 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) 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/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) 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)