diff --git a/build.gradle b/build.gradle index e19478d09..2420d96ec 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ mavenCentral() maven { name = "forge" - url = "https://files.minecraftforge.net/maven" + url = "https://maven.minecraftforge.net" } maven { name = "Sponge (Mixin)" @@ -14,7 +14,7 @@ } dependencies { classpath 'com.google.code.gson:gson:2.8.1' - classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.3' + classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.9' classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2' classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT' } @@ -23,6 +23,7 @@ plugins { id "checkstyle" id "jacoco" + id "maven-publish" id "com.github.hierynomus.license" version "0.15.0" id "com.matthewprenger.cursegradle" version "1.4.0" id "com.github.breadmoirai.github-release" version "2.2.12" @@ -31,8 +32,6 @@ apply plugin: 'net.minecraftforge.gradle' apply plugin: 'org.spongepowered.mixin' -apply plugin: 'maven-publish' -apply plugin: 'maven' version = mod_version @@ -43,6 +42,9 @@ toolchain { languageVersion = JavaLanguageVersion.of(8) } + + withSourcesJar() + withJavadocJar() } minecraft { @@ -127,7 +129,6 @@ accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg') configurations { shade compile.extendsFrom shade - deployerJars cctJavadoc } @@ -153,8 +154,7 @@ accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg') testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72' testImplementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' - - deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0" + testAnnotationProcessor 'org.spongepowered:mixin:0.8.2:processor' cctJavadoc 'cc.tweaked:cct-javadoc:1.3.0' } @@ -183,8 +183,6 @@ task luaJavadoc(type: Javadoc) { } jar { - dependsOn javadoc - manifest { attributes(["Specification-Title": "computercraft", "Specification-Vendor": "SquidDev", @@ -195,10 +193,6 @@ task luaJavadoc(type: Javadoc) { "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")]) } - from (sourceSets.main.allSource) { - include "dan200/computercraft/api/**/*.java" - } - from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) } } @@ -568,51 +562,41 @@ task setupServer(type: Copy) { publishing { publications { - mavenJava(MavenPublication) { + maven(MavenPublication) { from components.java - // artifact sourceJar + + pom { + name = 'CC: Tweaked' + description = 'CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.' + url = 'https://github.com/SquidDev-CC/CC-Tweaked' + + scm { + url = 'https://github.com/SquidDev-CC/CC-Tweaked.git' + } + + issueManagement { + system = 'github' + url = 'https://github.com/SquidDev-CC/CC-Tweaked/issues' + } + + licenses { + license { + name = 'ComputerCraft Public License, Version 1.0' + url = 'https://github.com/SquidDev-CC/CC-Tweaked/blob/mc-1.15.x/LICENSE' + } + } + } } } -} -uploadArchives { repositories { - if(project.hasProperty('mavenUploadUrl')) { - mavenDeployer { - configuration = configurations.deployerJars - - repository(url: project.property('mavenUploadUrl')) { - authentication( - userName: project.property('mavenUploadUser'), - privateKey: project.property('mavenUploadKey')) - } - - pom.project { - name 'CC: Tweaked' - packaging 'jar' - description 'CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.' - url 'https://github.com/SquidDev-CC/CC-Tweaked' - - scm { - url 'https://github.com/SquidDev-CC/CC-Tweaked.git' - } - - issueManagement { - system 'github' - url 'https://github.com/SquidDev-CC/CC-Tweaked/issues' - } - - licenses { - license { - name 'ComputerCraft Public License, Version 1.0' - url 'https://github.com/SquidDev-CC/CC-Tweaked/blob/master/LICENSE' - distribution 'repo' - } - } - } - - pom.whenConfigured { pom -> - pom.dependencies.clear() + if (project.hasProperty("mavenUser")) { + maven { + name = "SquidDev" + url = "https://squiddev.cc/maven" + credentials { + username = project.property("mavenUser") as String + password = project.property("mavenPass") as String } } } @@ -643,7 +627,7 @@ task setupServer(type: Copy) { prerelease false } -def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"] +def uploadTasks = ["publish", "curseforge", "githubRelease"] uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease } task uploadAll(dependsOn: uploadTasks) { diff --git a/doc/stub/turtle.lua b/doc/stub/turtle.lua index f5668f1ae..a2c0eef55 100644 --- a/doc/stub/turtle.lua +++ b/doc/stub/turtle.lua @@ -1 +1,13 @@ +--[[- Craft a recipe based on the turtle's inventory. + +The turtle's inventory should set up like a crafting grid. For instance, to +craft sticks, slots 1 and 5 should contain sticks. _All_ other slots should be +empty, including those outside the crafting "grid". + +@tparam[opt=64] number limit The maximum number of crafting steps to run. +@throws When limit is less than 1 or greater than 64. +@treturn[1] true If crafting succeeds. +@treturn[2] false If crafting fails. +@treturn string A string describing why crafting failed. +]] function craft(limit) end diff --git a/illuaminate.sexp b/illuaminate.sexp index 61f671582..22d222f7e 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -88,27 +88,16 @@ ;; Suppress warnings for currently undocumented modules. (at - (; Java APIs - /doc/stub/http.lua - /doc/stub/os.lua - /doc/stub/turtle.lua - /doc/stub/global.lua - ; Java generated APIs - /build/docs/luaJavadoc/turtle.lua - ; Peripherals - /build/docs/luaJavadoc/drive.lua - /build/docs/luaJavadoc/speaker.lua - /build/docs/luaJavadoc/printer.lua - ; Generic peripherals - /build/docs/luaJavadoc/fluid_storage.lua - ; Lua APIs + (; Lua APIs /src/main/resources/*/computercraft/lua/rom/apis/io.lua /src/main/resources/*/computercraft/lua/rom/apis/window.lua) (linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return)) -;; Suppress warnings for the BIOS using its own deprecated members for now. -(at /src/main/resources/*/computercraft/lua/bios.lua +;; Suppress warnings for various APIs using its own deprecated members. +(at + (/src/main/resources/*/computercraft/lua/bios.lua + /src/main/resources/*/computercraft/lua/rom/apis/turtle/turtle.lua) (linters -var:deprecated)) (at /src/test/resources/test-rom diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java index f429ca946..2c4223725 100644 --- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java +++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java @@ -98,7 +98,9 @@ public static IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull * resource folder onto a computer's file system. * * The files in this mount will be a combination of files in all mod jar, and data packs that contain - * resources with the same domain and path. + * resources with the same domain and path. For instance, ComputerCraft's resources are stored in + * "/data/computercraft/lua/rom". We construct a mount for that with + * {@code createResourceMount("computercraft", "lua/rom")}. * * @param domain The domain under which to look for resources. eg: "mymod". * @param subPath The subPath under which to look for resources. eg: "lua/myfiles". diff --git a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java index ed21df789..279871e50 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java @@ -103,9 +103,13 @@ private ResourceMount( String namespace, String subPath, IReloadableResourceMana private void load() { boolean hasAny = false; + String existingNamespace = null; + FileEntry newRoot = new FileEntry( new ResourceLocation( namespace, subPath ) ); for( ResourceLocation file : manager.listResources( subPath, s -> true ) ) { + existingNamespace = file.getNamespace(); + if( !file.getNamespace().equals( namespace ) ) continue; String localPath = FileSystem.toLocal( file.getPath(), subPath ); @@ -114,6 +118,15 @@ private void load() } root = hasAny ? newRoot : null; + + if( !hasAny ) + { + ComputerCraft.log.warn( "Cannot find any files under /data/{}/{} for resource mount.", namespace, subPath ); + if( newRoot != null ) + { + ComputerCraft.log.warn( "There are files under /data/{}/{} though. Did you get the wrong namespace?", existingNamespace, subPath ); + } + } } private FileEntry get( String path ) diff --git a/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java b/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java index e855ff5d1..d042a2b34 100644 --- a/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java +++ b/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java @@ -12,46 +12,12 @@ public class TextBuffer public TextBuffer( char c, int length ) { text = new char[length]; - for( int i = 0; i < length; i++ ) - { - text[i] = c; - } + this.fill( c ); } public TextBuffer( String text ) { - this( text, 1 ); - } - - public TextBuffer( String text, int repetitions ) - { - int textLength = text.length(); - this.text = new char[textLength * repetitions]; - for( int i = 0; i < repetitions; i++ ) - { - for( int j = 0; j < textLength; j++ ) - { - this.text[j + i * textLength] = text.charAt( j ); - } - } - } - - public TextBuffer( TextBuffer text ) - { - this( text, 1 ); - } - - public TextBuffer( TextBuffer text, int repetitions ) - { - int textLength = text.length(); - this.text = new char[textLength * repetitions]; - for( int i = 0; i < repetitions; i++ ) - { - for( int j = 0; j < textLength; j++ ) - { - this.text[j + i * textLength] = text.charAt( j ); - } - } + this.text = text.toCharArray(); } public int length() @@ -59,39 +25,16 @@ public int length() return text.length; } - public String read() - { - return read( 0, text.length ); - } - - public String read( int start ) - { - return read( start, text.length ); - } - - public String read( int start, int end ) - { - start = Math.max( start, 0 ); - end = Math.min( end, text.length ); - int textLength = Math.max( end - start, 0 ); - return new String( text, start, textLength ); - } - public void write( String text ) { - write( text, 0, text.length() ); + write( text, 0 ); } public void write( String text, int start ) - { - write( text, start, start + text.length() ); - } - - public void write( String text, int start, int end ) { int pos = start; start = Math.max( start, 0 ); - end = Math.min( end, pos + text.length() ); + int end = Math.min( start + text.length(), pos + text.length() ); end = Math.min( end, this.text.length ); for( int i = start; i < end; i++ ) { @@ -101,23 +44,10 @@ public void write( String text, int start, int end ) public void write( TextBuffer text ) { - write( text, 0, text.length() ); - } - - public void write( TextBuffer text, int start ) - { - write( text, start, start + text.length() ); - } - - public void write( TextBuffer text, int start, int end ) - { - int pos = start; - start = Math.max( start, 0 ); - end = Math.min( end, pos + text.length() ); - end = Math.min( end, this.text.length ); - for( int i = start; i < end; i++ ) + int end = Math.min( text.length(), this.text.length ); + for( int i = 0; i < end; i++ ) { - this.text[i] = text.charAt( i - pos ); + this.text[i] = text.charAt( i ); } } @@ -126,11 +56,6 @@ public void fill( char c ) fill( c, 0, text.length ); } - public void fill( char c, int start ) - { - fill( c, start, text.length ); - } - public void fill( char c, int start, int end ) { start = Math.max( start, 0 ); @@ -141,52 +66,6 @@ public void fill( char c, int start, int end ) } } - public void fill( String text ) - { - fill( text, 0, this.text.length ); - } - - public void fill( String text, int start ) - { - fill( text, start, this.text.length ); - } - - public void fill( String text, int start, int end ) - { - int pos = start; - start = Math.max( start, 0 ); - end = Math.min( end, this.text.length ); - - int textLength = text.length(); - for( int i = start; i < end; i++ ) - { - this.text[i] = text.charAt( (i - pos) % textLength ); - } - } - - public void fill( TextBuffer text ) - { - fill( text, 0, this.text.length ); - } - - public void fill( TextBuffer text, int start ) - { - fill( text, start, this.text.length ); - } - - public void fill( TextBuffer text, int start, int end ) - { - int pos = start; - start = Math.max( start, 0 ); - end = Math.min( end, this.text.length ); - - int textLength = text.length(); - for( int i = start; i < end; i++ ) - { - this.text[i] = text.charAt( (i - pos) % textLength ); - } - } - public char charAt( int i ) { return text[i]; diff --git a/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java index a6ea2fc91..ae8f9589f 100644 --- a/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java @@ -114,10 +114,10 @@ public void onRuntimeAvailable( IJeiRuntime runtime ) */ private static final ISubtypeInterpreter turtleSubtype = stack -> { Item item = stack.getItem(); - if( !(item instanceof ITurtleItem) ) return ""; + if( !(item instanceof ITurtleItem) ) return ISubtypeInterpreter.NONE; ITurtleItem turtle = (ITurtleItem) item; - StringBuilder name = new StringBuilder(); + StringBuilder name = new StringBuilder( "turtle:" ); // Add left and right upgrades to the identifier ITurtleUpgrade left = turtle.getUpgrade( stack, TurtleSide.LEFT ); @@ -134,9 +134,9 @@ public void onRuntimeAvailable( IJeiRuntime runtime ) */ private static final ISubtypeInterpreter pocketSubtype = stack -> { Item item = stack.getItem(); - if( !(item instanceof ItemPocketComputer) ) return ""; + if( !(item instanceof ItemPocketComputer) ) return ISubtypeInterpreter.NONE; - StringBuilder name = new StringBuilder(); + StringBuilder name = new StringBuilder( "pocket:" ); // Add the upgrade to the identifier IPocketUpgrade upgrade = ItemPocketComputer.getUpgrade( stack ); @@ -150,11 +150,11 @@ public void onRuntimeAvailable( IJeiRuntime runtime ) */ private static final ISubtypeInterpreter diskSubtype = stack -> { Item item = stack.getItem(); - if( !(item instanceof ItemDisk) ) return ""; + if( !(item instanceof ItemDisk) ) return ISubtypeInterpreter.NONE; ItemDisk disk = (ItemDisk) item; int colour = disk.getColour( stack ); - return colour == -1 ? "" : String.format( "%06x", colour ); + return colour == -1 ? ISubtypeInterpreter.NONE : String.format( "%06x", colour ); }; } diff --git a/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java b/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java index a3a4810d6..2050a83e7 100644 --- a/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java +++ b/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java @@ -200,7 +200,7 @@ else if( stack.getItem() instanceof ItemPocketComputer ) for( UpgradeInfo upgrade : upgrades ) { ItemStack craftingStack = upgrade.stack; - if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.upgrade.isItemSuitable( stack ) ) + if( craftingStack.isEmpty() || craftingStack.getItem() != stack.getItem() || !upgrade.upgrade.isItemSuitable( stack ) ) { continue; } @@ -319,13 +319,6 @@ private static class Shaped extends ShapedRecipe super( ID, null, width, height, input, output ); } - @Nonnull - @Override - public ResourceLocation getId() - { - return null; - } - @Nonnull @Override public IRecipeSerializer getSerializer() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java index a9d206f5a..9c994aa6e 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java @@ -45,6 +45,19 @@ public ResourceLocation id() return new ResourceLocation( ForgeVersion.MOD_ID, "fluid" ); } + /** + * Get all "tanks" in this fluid storage. + * + * Each tank either contains some amount of fluid or is empty. Tanks with fluids inside will return some basic + * information about the fluid, including its name and amount. + * + * The returned table is sparse, and so empty tanks will be `nil` - it is recommended to loop over using `pairs` + * rather than `ipairs`. + * + * @param fluids The current fluid handler. + * @return All tanks. + * @cc.treturn { (table|nil)... } All tanks in this fluid storage. + */ @LuaFunction( mainThread = true ) public static Map> tanks( IFluidHandler fluids ) { @@ -59,6 +72,22 @@ public ResourceLocation id() return result; } + /** + * Move a fluid from one fluid container to another connected one. + * + * This allows you to pull fluid in the current fluid container to another container on the same wired + * network. Both containers must attached to wired modems which are connected via a cable. + * + * @param from Container to move fluid from. + * @param computer The current computer. + * @param toName The name of the peripheral/container to push to. This is the string given to @{peripheral.wrap}, + * and displayed by the wired modem. + * @param limit The maximum amount of fluid to move. + * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen. + * @return The amount of moved fluid. + * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container. + * @cc.see peripheral.getName Allows you to get the name of a @{peripheral.wrap|wrapped} peripheral. + */ @LuaFunction( mainThread = true ) public static int pushFluid( IFluidHandler from, IComputerAccess computer, @@ -84,6 +113,22 @@ public static int pushFluid( : moveFluid( from, new FluidStack( fluid, actualLimit ), to ); } + /** + * Move a fluid from a connected fluid container into this oneone. + * + * This allows you to pull fluid in the current fluid container from another container on the same wired + * network. Both containers must attached to wired modems which are connected via a cable. + * + * @param to Container to move fluid to. + * @param computer The current computer. + * @param fromName The name of the peripheral/container to push to. This is the string given to @{peripheral.wrap}, + * and displayed by the wired modem. + * @param limit The maximum amount of fluid to move. + * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen. + * @return The amount of moved fluid. + * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container. + * @cc.see peripheral.getName Allows you to get the name of a @{peripheral.wrap|wrapped} peripheral. + */ @LuaFunction( mainThread = true ) public static int pullFluid( IFluidHandler to, IComputerAccess computer, diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java index 0663d66ad..64e342b30 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java @@ -65,8 +65,8 @@ public static int size( IItemHandler inventory ) * {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail} includes. More information can be fetched * with {@link #getItemDetail}. * - * The table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs` rather than - * `ipairs`. + * The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs` + * rather than `ipairs`. * * @param inventory The current inventory. * @return All items in this inventory. diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 671c9a3b7..6c87be3d2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -371,18 +371,36 @@ public final MethodResult detectDown() return trackCommand( new TurtleDetectCommand( InteractDirection.DOWN ) ); } + /** + * Check if the block in front of the turtle is equal to the item in the currently selected slot. + * + * @return If the block and item are equal. + * @cc.treturn boolean If the block and item are equal. + */ @LuaFunction public final MethodResult compare() { return trackCommand( new TurtleCompareCommand( InteractDirection.FORWARD ) ); } + /** + * Check if the block above the turtle is equal to the item in the currently selected slot. + * + * @return If the block and item are equal. + * @cc.treturn boolean If the block and item are equal. + */ @LuaFunction public final MethodResult compareUp() { return trackCommand( new TurtleCompareCommand( InteractDirection.UP ) ); } + /** + * Check if the block below the turtle is equal to the item in the currently selected slot. + * + * @return If the block and item are equal. + * @cc.treturn boolean If the block and item are equal. + */ @LuaFunction public final MethodResult compareDown() { @@ -478,12 +496,57 @@ public final MethodResult suckDown( Optional count ) throws LuaExceptio return trackCommand( new TurtleSuckCommand( InteractDirection.DOWN, checkCount( count ) ) ); } + /** + * Get the maximum amount of fuel this turtle currently holds. + * + * @return The fuel level, or "unlimited". + * @cc.treturn [1] number The current amount of fuel a turtle this turtle has. + * @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving. + * @see #getFuelLimit() + * @see #refuel(Optional) + */ @LuaFunction public final Object getFuelLevel() { return turtle.isFuelNeeded() ? turtle.getFuelLevel() : "unlimited"; } + /** + * Refuel this turtle. + * + * While most actions a turtle can perform (such as digging or placing blocks), moving consumes fuel from the + * turtle's internal buffer. If a turtle has no fuel, it will not move. + * + * {@link #refuel} refuels the turtle, consuming fuel items (such as coal or lava buckets) from the currently + * selected slot and converting them into energy. This finishes once the turtle is fully refuelled or all items have + * been consumed. + * + * @param countA The maximum number of items to consume. One can pass `0` to check if an item is combustable or not. + * @return If this turtle could be refuelled. + * @throws LuaException If the refuel count is out of range. + * @cc.treturn [1] true If the turtle was refuelled. + * @cc.treturn [2] false If the turtle was not refuelled. + * @cc.treturn [2] string The reason the turtle was not refuelled ( + * @cc.usage Refuel a turtle from the currently selected slot. + *
{@code
+     * local level = turtle.getFuelLevel()
+     * if new_level == "unlimited" then error("Turtle does not need fuel", 0) end
+     *
+     * local ok, err = turtle.refuel()
+     * if ok then
+     *   local new_level = turtle.getFuelLevel()
+     *   print(("Refuelled %d, current level is %d"):format(new_level - level, new_level))
+     * else
+     *   printError(err)
+     * end}
+ * @cc.usage Check if the current item is a valid fuel source. + *
{@code
+     * local is_fuel, reason = turtle.refuel(0)
+     * if not is_fuel then printError(reason) end
+     * }
+ * @see #getFuelLevel() + * @see #getFuelLimit() + */ @LuaFunction public final MethodResult refuel( Optional countA ) throws LuaException { @@ -492,12 +555,30 @@ public final MethodResult refuel( Optional countA ) throws LuaException return trackCommand( new TurtleRefuelCommand( count ) ); } + /** + * Compare the item in the currently selected slot to the item in another slot. + * + * @param slot The slot to compare to. + * @return If the items are the same. + * @throws LuaException If the slot is out of range. + * @cc.treturn boolean If the two items are equal. + */ @LuaFunction public final MethodResult compareTo( int slot ) throws LuaException { return trackCommand( new TurtleCompareToCommand( checkSlot( slot ) ) ); } + /** + * Move an item from the selected slot to another one. + * + * @param slotArg The slot to move this item to. + * @param countArg The maximum number of items to move. + * @return If the item was moved or not. + * @throws LuaException If the slot is out of range. + * @throws LuaException If the number of items is out of range. + * @cc.treturn boolean If some items were successfully moved. + */ @LuaFunction public final MethodResult transferTo( int slotArg, Optional countArg ) throws LuaException { @@ -518,18 +599,55 @@ public final int getSelectedSlot() return turtle.getSelectedSlot() + 1; } + /** + * Get the maximum amount of fuel this turtle can hold. + * + * By default, normal turtles have a limit of 20,000 and advanced turtles of 100,000. + * + * @return The limit, or "unlimited". + * @cc.treturn [1] number The maximum amount of fuel a turtle can hold. + * @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving. + * @see #getFuelLevel() + * @see #refuel(Optional) + */ @LuaFunction public final Object getFuelLimit() { return turtle.isFuelNeeded() ? turtle.getFuelLimit() : "unlimited"; } + /** + * Equip (or unequip) an item on the left side of this turtle. + * + * This finds the item in the currently selected slot and attempts to equip it to the left side of the turtle. The + * previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous + * upgrade is removed, but no new one is equipped. + * + * @return Whether an item was equiped or not. + * @cc.treturn [1] true If the item was equipped. + * @cc.treturn [2] false If we could not equip the item. + * @cc.treturn [2] string The reason equipping this item failed. + * @see #equipRight() + */ @LuaFunction public final MethodResult equipLeft() { return trackCommand( new TurtleEquipCommand( TurtleSide.LEFT ) ); } + /** + * Equip (or unequip) an item on the right side of this turtle. + * + * This finds the item in the currently selected slot and attempts to equip it to the right side of the turtle. The + * previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous + * upgrade is removed, but no new one is equipped. + * + * @return Whether an item was equiped or not. + * @cc.treturn [1] true If the item was equipped. + * @cc.treturn [2] false If we could not equip the item. + * @cc.treturn [2] string The reason equipping this item failed. + * @see #equipRight() + */ @LuaFunction public final MethodResult equipRight() { 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 5ecbdf652..4898df308 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/io.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/io.lua @@ -137,6 +137,15 @@ handleMetatable = { return handle.seek(whence, offset) end, + --[[- Sets the buffering mode for an output file. + + This has no effect under ComputerCraft, and exists with compatility + with base Lua. + @tparam string mode The buffering mode. + @tparam[opt] number size The size of the buffer. + @see file:setvbuf Lua's documentation for `setvbuf`. + @deprecated This has no effect in CC. + ]] setvbuf = function(self, mode, size) end, --- Write one or more values to the file 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 b920341c3..f6f1efaac 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua @@ -132,7 +132,17 @@ function drawLine(startX, startY, endX, endY, colour) return end - local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY) + 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 -- TODO: clip to screen rectangle? 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 c284d0691..0f3478046 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua @@ -161,7 +161,9 @@ end -- @tparam string name The name of the peripheral to wrap. -- @treturn table|nil The table containing the peripheral's methods, or `nil` if -- there is no peripheral present with the given name. --- @usage peripheral.wrap("top").open(1) +-- @usage Open the modem on the top of this computer. +-- +-- peripheral.wrap("top").open(1) function wrap(name) expect(1, name, "string") @@ -183,16 +185,25 @@ function wrap(name) return result end ---- Find all peripherals of a specific type, and return the --- @{peripheral.wrap|wrapped} peripherals. --- --- @tparam string ty The type of peripheral to look for. --- @tparam[opt] function(name:string, wrapped:table):boolean filter A --- 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 { peripheral.find("monitor") } --- @usage peripheral.find("modem", rednet.open) +--[[- Find all peripherals of a specific type, and return the +@{peripheral.wrap|wrapped} peripherals. + +@tparam string ty The type of peripheral to look for. +@tparam[opt] function(name:string, wrapped:table):boolean filter A +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 Find all monitors and store them in a table, writing "Hello" on each one. + + local monitors = { peripheral.find("monitor") } + for _, monitor in pairs(monitors) do + monitor.write("Hello") + end + +@usage This abuses the `filter` argument to call @{rednet.open} on every modem. + + peripheral.find("modem", rednet.open) +]] function find(ty, filter) expect(1, ty, "string") expect(2, filter, "function", "nil") diff --git a/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua b/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua index c9d57bf12..0a66add16 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua @@ -10,6 +10,7 @@ end -- -- Generally you should not need to use this table - it only exists for -- backwards compatibility reasons. +-- @deprecated native = turtle.native or turtle local function addCraftMethod(object) 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 9a3aa7b0a..f202c204e 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 @@ -1,23 +1,28 @@ ---- Provides a "pretty printer", for rendering data structures in an --- aesthetically pleasing manner. --- --- In order to display something using @{cc.pretty}, you build up a series of --- @{Doc|documents}. These behave a little bit like strings; you can concatenate --- them together and then print them to the screen. --- --- However, documents also allow you to control how they should be printed. There --- are several functions (such as @{nest} and @{group}) which allow you to control --- the "layout" of the document. When you come to display the document, the 'best' --- (most compact) layout is used. --- --- @module cc.pretty --- @usage Print a table to the terminal --- local pretty = require "cc.pretty" --- pretty.print(pretty.pretty({ 1, 2, 3 })) --- --- @usage Build a custom document and display it --- local pretty = require "cc.pretty" --- pretty.print(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) +--[[- Provides a "pretty printer", for rendering data structures in an +aesthetically pleasing manner. + +In order to display something using @{cc.pretty}, you build up a series of +@{Doc|documents}. These behave a little bit like strings; you can concatenate +them together and then print them to the screen. + +However, documents also allow you to control how they should be printed. There +are several functions (such as @{nest} and @{group}) which allow you to control +the "layout" of the document. When you come to display the document, the 'best' +(most compact) layout is used. + +The structure of this module is based on [A Prettier Printer][prettier]. + +[prettier]: https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf "A Prettier Printer" + +@module cc.pretty +@usage Print a table to the terminal + local pretty = require "cc.pretty" + pretty.print(pretty.pretty({ 1, 2, 3 })) + +@usage Build a custom document and display it + local pretty = require "cc.pretty" + 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/programs/edit.lua b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua index f67bd0ede..c913aa234 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua @@ -801,6 +801,7 @@ while bRunning do end else bMenu = false + term.setCursorBlink(true) redrawMenu() end end diff --git a/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java b/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java new file mode 100644 index 000000000..286e62ad6 --- /dev/null +++ b/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java @@ -0,0 +1,150 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.core.terminal; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TextBufferTest +{ + @Test + void testStringConstructor() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + assertEquals( "test", textBuffer.toString() ); + } + + @Test + void testCharRepetitionConstructor() + { + TextBuffer textBuffer = new TextBuffer( 'a', 5 ); + assertEquals( "aaaaa", textBuffer.toString() ); + } + + @Test + void testLength() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + assertEquals( 4, textBuffer.length() ); + } + + @Test + void testWrite() + { + TextBuffer textBuffer = new TextBuffer( ' ', 4 ); + textBuffer.write( "test" ); + assertEquals( "test", textBuffer.toString() ); + } + + @Test + void testWriteTextBuffer() + { + TextBuffer source = new TextBuffer( "test" ); + TextBuffer target = new TextBuffer( " " ); + target.write( source ); + assertEquals( "test", target.toString() ); + } + + @Test + void testWriteFromPos() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.write( "il", 1 ); + assertEquals( "tilt", textBuffer.toString() ); + } + + @Test + void testWriteOutOfBounds() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.write( "abcdefghijklmnop", -5 ); + assertEquals( "fghi", textBuffer.toString() ); + } + + @Test + void testWriteOutOfBounds2() + { + TextBuffer textBuffer = new TextBuffer( " " ); + textBuffer.write( "Hello, world!", -3 ); + assertEquals( "lo, world! ", textBuffer.toString() ); + } + + @Test + void testFill() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.fill( 'c' ); + assertEquals( "cccc", textBuffer.toString() ); + } + + @Test + void testFillSubstring() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.fill( 'c', 1, 3 ); + assertEquals( "tcct", textBuffer.toString() ); + } + + @Test + void testFillOutOfBounds() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.fill( 'c', -5, 5 ); + assertEquals( "cccc", textBuffer.toString() ); + } + + @Test + void testCharAt() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + assertEquals( 'e', textBuffer.charAt( 1 ) ); + } + + @Test + void testSetChar() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.setChar( 2, 'n' ); + assertEquals( "tent", textBuffer.toString() ); + } + + @Test + void testSetCharWithNegativeIndex() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.setChar( -5, 'n' ); + assertEquals( "test", textBuffer.toString(), "Buffer should not change after setting char with negative index." ); + } + + @Test + void testSetCharWithIndexBeyondBufferEnd() + { + TextBuffer textBuffer = new TextBuffer( "test" ); + textBuffer.setChar( 10, 'n' ); + assertEquals( "test", textBuffer.toString(), "Buffer should not change after setting char beyond buffer end." ); + } + + @Test + void testMultipleOperations() + { + TextBuffer textBuffer = new TextBuffer( ' ', 5 ); + textBuffer.setChar( 0, 'H' ); + textBuffer.setChar( 1, 'e' ); + textBuffer.setChar( 2, 'l' ); + textBuffer.write( "lo", 3 ); + assertEquals( "Hello", textBuffer.toString(), "TextBuffer failed to persist over multiple operations." ); + } + + @Test + void testEmptyBuffer() + { + TextBuffer textBuffer = new TextBuffer( "" ); + // exception on writing to empty buffer would fail the test + textBuffer.write( "test" ); + assertEquals( "", textBuffer.toString() ); + } +} 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 fc2b6008c..f13bbcfd2 100644 --- a/src/test/resources/test-rom/spec/apis/paintutils_spec.lua +++ b/src/test/resources/test-rom/spec/apis/paintutils_spec.lua @@ -67,6 +67,19 @@ describe("The paintutils library", function() { " ", "000", "ffe" }, }) end) + + it("draws a line going diagonally from bottom left", function() + local w = with_window(3, 3, function() + term.setBackgroundColour(colours.red) + paintutils.drawLine(1, 3, 3, 1) + end) + + window_eq(w, { + { " ", "000", "ffe" }, + { " ", "000", "fef" }, + { " ", "000", "eff" }, + }) + end) end) describe("paintutils.drawBox", function()