1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-23 09:57:39 +00:00

Compare commits

..

28 Commits

Author SHA1 Message Date
Merith-TK
d8b0037cfa CC:R 1.94.1-beta
Update version info, re-brand from CC:Tweaked For Fabric, to CC:Restitched
2021-03-28 22:49:15 -07:00
Merith-TK
91a9e7fdf7 Update .gitignore 2021-03-27 16:40:59 -07:00
Merith-TK
2c3573719b [Patchwork] Fix Network Config
Network config should now work in a stable mannor, the previous person did not port over the imports and thats why this broke in the first place, possibly required import did not exist for fabric at the time?
2021-03-27 15:19:09 -07:00
Devan-Kerman
46bc42d5a7 Update README.md 2021-03-04 19:23:36 -06:00
Devan-Kerman
1b9e93a80f Merge pull request #31 from davidqueneau/fabric
Revert "Cable modems can be placed against all blocks, including ches…
2021-03-04 19:23:07 -06:00
Devan-Kerman
7b93f91ffa Merge pull request #36 from Merith-TK/fabric
Update to Match CC:T version
2021-03-04 19:22:59 -06:00
Merith-TK
31a1027401 Clean up some examples a little bit
Would be good if they didn't crash and burn on entry :).
2021-03-02 23:12:53 -08:00
Merith-TK
e0fc994819 [TODO] Auto-generate monitor models
I didn't think it was worth it, and then I found myself needing to
update a dozen of them. The code isn't especially pretty, but it works,
so that's fine.

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

[1] https://github.com/SquidDev-CC/CC-Tweaked/wiki/Monitor-texture-reference
2021-03-02 22:47:44 -08:00
Merith-TK
43408bf085 CC:R 1.94.0 2021-03-02 19:21:12 -08:00
Merith-TK
d28f42e8b7 use arg[0] in all usage printouts (#571) 2021-03-02 18:22:26 -08:00
Merith-TK
ac452582c1 Use blit to draw boxes, add colors.toBlit (#570) 2021-02-22 18:49:39 -08:00
Merith-TK
7e65c6b25c Fix JSON objects failing to pass 2021-02-22 18:11:27 -08:00
Merith-TK
457a863842 Dont fail when codecov is being finicky 2021-02-22 18:09:31 -08:00
Merith-TK
eef36e1358 Various SNBT parsing improvements 2021-02-22 17:45:44 -08:00
Merith-TK
feda08b915 Draw in-hand pocket computers with blending 2021-02-22 17:43:54 -08:00
Merith-TK
0240ce50ce Bump cct-javadoc version 2021-02-22 17:34:20 -08:00
Merith-TK
592b83e784 [TODO] Fix players not getting advancements when they own turtles 2021-02-22 17:32:26 -08:00
Merith-TK
5d91491ec7 Remove superfluous imports 2021-02-22 12:25:13 -08:00
Merith-TK
7326d1110d Make generic peripherals on by default 2021-02-22 12:17:03 -08:00
Merith-TK
0aa6ac05a0 Add function to get window visibility 2021-02-22 11:56:22 -08:00
Merith-TK
27a2c063b9 Update configuration to match latest illuaminate 2021-02-22 11:50:33 -08:00
Merith-TK
89a195ec06 CC:R 1.93.1 2021-02-22 11:05:23 -08:00
Merith-TK
0e5fd4e8e0 Fix TBO norm issues on old GPUs 2021-02-22 10:56:52 -08:00
Merith-TK
aa4ec53bb6 Bump JEI/crafttweaker versions 2021-02-22 10:50:57 -08:00
Merith-TK
6b49327462 Document remaining OS functions (#554) 2021-02-22 10:49:52 -08:00
Merith-TK
74ad934889 Add color table to docs (#553) 2021-02-22 10:43:27 -08:00
Merith-TK
08b3dbbad5 Fix My Docs, Update Patchwork Format 2021-02-22 10:38:52 -08:00
David Queneau
00b458c39a Revert "Cable modems can be placed against all blocks, including chests."
This reverts commit 664df62d
2021-02-07 10:47:50 -08:00
70 changed files with 1369 additions and 305 deletions

50
.github/workflows/main-ci.yml vendored Normal file
View File

@@ -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-restiched
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

5
.gitignore vendored
View File

@@ -9,6 +9,11 @@
/run-*
/test-files
# Autogenerated by IDE
/bin
/.settings
.classpath
*.ipr
*.iws
*.iml

View File

@@ -2,8 +2,4 @@
# This is a Work In Progress Port
*it runs and works-ish*
## Reached Parity with CC:T 1.93.0
THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T
The changelog is located at [PatchWork.md](patchwork.md)
PRs welcome

View File

@@ -9,7 +9,7 @@ targetCompatibility = JavaVersion.VERSION_1_8
version = mod_version
group = "dan200.computercraft"
archivesBaseName = "cc-tweaked-fabric-${mc_version}"
archivesBaseName = "cc-restiched"
repositories {
mavenCentral()
@@ -29,6 +29,8 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
compile 'com.electronwill.night-config:json:3.6.0'
modImplementation "me.shedaniel.cloth:config-2:${cloth_config_version}"
modImplementation "io.github.prospector:modmenu:${modmenu_version}"

View File

@@ -2,7 +2,7 @@
org.gradle.jvmargs=-Xmx1G
# Mod properties
mod_version=1.93.0
mod_version=1.94.1-beta
# Minecraft properties
mc_version=1.16.2

View File

@@ -11,6 +11,8 @@ SubScript // Desc of commit
If a edit that is present in CC:T is not needed, I will skip over it.
Any and all references to an issue number, are to be found on CC:T's repo.
Any commit that starts with `[Patchwork]` are purely edits made by my hand, and not based on other commits from CC:T, this is to help differentiate my changes from the official changes
Lines that are found above a commit in this log like this one, (excluding this one) are comments about how i had to implement things that are not a simple 1:1 (excluding fabric/forge differences) conversion
```md
@@ -122,3 +124,190 @@ 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 <lmao@distruzione.org>
```
[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 :).
```

View File

@@ -9,4 +9,4 @@ pluginManagement {
}
}
rootProject.name = "cc-tweaked-fabric-${mc_version}"
rootProject.name = "cc-restiched"

View File

@@ -135,7 +135,7 @@ public interface ITurtleAccess {
*
* @return This turtle's owner.
*/
@Nonnull
@Nullable
GameProfile getOwningPlayer();
/**

View File

@@ -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;

View File

@@ -6,120 +6,129 @@
package dan200.computercraft.core.apis.http.options;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.Config;
import com.electronwill.nightconfig.core.InMemoryCommentedFormat;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dan200.computercraft.ComputerCraft;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
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 );
config.add( "host", host );
config.add( "action", action.name().toLowerCase( Locale.ROOT ) );
// public static UnmodifiableConfig makeRule( String host, Action action )
// {
// CommentedConfig config = InMemoryCommentedFormat.defaultInstance().createConfig( ConcurrentHashMap::new );
// config.add( "host", host );
// config.add( "action", action.name().toLowerCase( Locale.ROOT ) );
//
// if( host.equals( "*" ) && action == Action.ALLOW )
// {
// config.setComment( "timeout", "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." );
// config.add( "timeout", AddressRule.TIMEOUT );
//
// config.setComment( "max_download", "The maximum size (in bytes) that a computer can download in a single request. Note that responses
// may receive more data than allowed, but this data will not be returned to the client." );
// config.set( "max_download", AddressRule.MAX_DOWNLOAD );
//
// config.setComment( "max_upload", "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and
// POST text." );
// config.set( "max_upload", AddressRule.MAX_UPLOAD );
//
// config.setComment( "max_websocket_message", "The maximum size (in bytes) that a computer can send or receive in one websocket packet." );
// config.set( "max_websocket_message", AddressRule.WEBSOCKET_MESSAGE );
// }
//
// return config;
// }
//
// public static boolean checkRule( UnmodifiableConfig builder )
// {
// 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, 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 );
// Integer timeout = get( builder, "timeout", Number.class ).map( Number::intValue ).orElse( null );
// Long maxUpload = get( builder, "max_upload", Number.class ).map( Number::longValue ).orElse( null );
// Long maxDownload = get( builder, "max_download", Number.class ).map( Number::longValue ).orElse( null );
// Integer websocketMessage = get( builder, "websocket_message", Number.class ).map( Number::intValue ).orElse( null );
//
// PartialOptions options = new PartialOptions(
// action,
// maxUpload,
// maxDownload,
// timeout,
// websocketMessage
// );
//
// return AddressRule.parse( hostObj, port, options );
// }
//
// private static <T> boolean check( UnmodifiableConfig config, String field, Class<T> klass )
// {
// Object value = config.get( field );
// if( value == null || klass.isInstance( value ) ) return true;
//
// ComputerCraft.log.warn( "HTTP rule's {} is not a {}.", field, klass.getSimpleName() );
// return false;
// }
//
// private static <T extends Enum<T>> boolean checkEnum( UnmodifiableConfig config, String field, Class<T> klass )
// {
// Object value = config.get( field );
// if( value == null ) return true;
//
// if( !(value instanceof String) )
// {
// ComputerCraft.log.warn( "HTTP rule's {} is not a string", field );
// return false;
// }
//
// if( parseEnum( klass, (String) value ) == null )
// {
// ComputerCraft.log.warn( "HTTP rule's {} is not a known option", field );
// return false;
// }
//
// return true;
// }
//
// private static <T> Optional<T> get( UnmodifiableConfig config, String field, Class<T> klass )
// {
// Object value = config.get( field );
// return klass.isInstance( value ) ? Optional.of( klass.cast( value ) ) : Optional.empty();
// }
//
// private static <T extends Enum<T>> Optional<T> getEnum( UnmodifiableConfig config, String field, Class<T> klass )
// {
// return get( config, field, String.class ).map( x -> parseEnum( klass, x ) );
// }
//
// @Nullable
// private static <T extends Enum<T>> T parseEnum( Class<T> klass, String x )
// {
// for( T value : klass.getEnumConstants() )
// {
// if( value.name().equalsIgnoreCase( x ) ) return value;
// }
// return null;
// }
if( host.equals( "*" ) && action == Action.ALLOW )
{
config.setComment( "timeout", "The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited." );
config.add( "timeout", AddressRule.TIMEOUT );
config.setComment( "max_download", "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." );
config.set( "max_download", AddressRule.MAX_DOWNLOAD );
config.setComment( "max_upload", "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." );
config.set( "max_upload", AddressRule.MAX_UPLOAD );
config.setComment( "max_websocket_message", "The maximum size (in bytes) that a computer can send or receive in one websocket packet." );
config.set( "max_websocket_message", AddressRule.WEBSOCKET_MESSAGE );
}
return config;
}
public static boolean checkRule( UnmodifiableConfig builder )
{
String hostObj = get( builder, "host", String.class ).orElse( null );
Integer port = get( builder, "port", Number.class ).map( Number::intValue ).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, 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 );
Integer timeout = get( builder, "timeout", Number.class ).map( Number::intValue ).orElse( null );
Long maxUpload = get( builder, "max_upload", Number.class ).map( Number::longValue ).orElse( null );
Long maxDownload = get( builder, "max_download", Number.class ).map( Number::longValue ).orElse( null );
Integer websocketMessage = get( builder, "websocket_message", Number.class ).map( Number::intValue ).orElse( null );
PartialOptions options = new PartialOptions(
action,
maxUpload,
maxDownload,
timeout,
websocketMessage
);
return AddressRule.parse( hostObj, port, options );
}
private static <T> boolean check( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
if( value == null || klass.isInstance( value ) ) return true;
ComputerCraft.log.warn( "HTTP rule's {} is not a {}.", field, klass.getSimpleName() );
return false;
}
private static <T extends Enum<T>> boolean checkEnum( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
if( value == null ) return true;
if( !(value instanceof String) )
{
ComputerCraft.log.warn( "HTTP rule's {} is not a string", field );
return false;
}
if( parseEnum( klass, (String) value ) == null )
{
ComputerCraft.log.warn( "HTTP rule's {} is not a known option", field );
return false;
}
return true;
}
private static <T> Optional<T> get( UnmodifiableConfig config, String field, Class<T> klass )
{
Object value = config.get( field );
return klass.isInstance( value ) ? Optional.of( klass.cast( value ) ) : Optional.empty();
}
private static <T extends Enum<T>> Optional<T> getEnum( UnmodifiableConfig config, String field, Class<T> klass )
{
return get( config, field, String.class ).map( x -> parseEnum( klass, x ) );
}
@Nullable
private static <T extends Enum<T>> T parseEnum( Class<T> klass, String x )
{
for( T value : klass.getEnumConstants() )
{
if( value.name().equalsIgnoreCase( x ) ) return value;
}
return null;
}
}

View File

@@ -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.
* <pre>
* commands.execAsync("~ ~1 ~ minecraft:stone")

View File

@@ -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

View File

@@ -181,19 +181,17 @@ public class BlockCable extends BlockGeneric implements Waterloggable {
// : new ItemStack( ComputerCraftRegistry.ModItems.CABLE.get() );
// }
// Commenting override to allow cable modems to be placed on chests, so that chests can be generic inventory peripherals.
// TODO Perhaps there is a more selective way to achieve this?
// @Override
// @Deprecated
// public boolean canPlaceAt(BlockState state, @Nonnull WorldView world, @Nonnull BlockPos pos) {
// Direction facing = state.get(MODEM)
// .getFacing();
// if (facing == null) {
// return true;
// }
//
// return sideCoversSmallSquare(world, pos.offset(facing), facing.getOpposite());
// }
@Override
@Deprecated
public boolean canPlaceAt(BlockState state, @Nonnull WorldView world, @Nonnull BlockPos pos) {
Direction facing = state.get(MODEM)
.getFacing();
if (facing == null) {
return true;
}
return sideCoversSmallSquare(world, pos.offset(facing), facing.getOpposite());
}
@Nonnull
@Override

View File

@@ -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);

View File

@@ -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;

View File

@@ -33,6 +33,7 @@ import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
@@ -42,13 +43,33 @@ import net.minecraft.util.math.Vec3d;
@SuppressWarnings ("EntityConstructor")
public final class TurtlePlayer extends FakePlayer {
private static final GameProfile DEFAULT_PROFILE = new GameProfile(UUID.fromString("0d0c4ca0-4ff1-11e4-916c-0800200c9a66"), "[ComputerCraft]");
private TurtlePlayer(ITurtleAccess turtle) {
super((ServerWorld) turtle.getWorld(), getProfile(turtle.getOwningPlayer()));
this.networkHandler = new FakeNetHandler(this);
this.setState(turtle);
// TODO [M3R1-01] Fix Turtle not giving player achievement for actions
private TurtlePlayer( ServerWorld world, GameProfile name )
{
super( world, name );
}
private static TurtlePlayer create( ITurtleAccess turtle )
{
ServerWorld world = (ServerWorld) turtle.getWorld();
GameProfile profile = turtle.getOwningPlayer();
TurtlePlayer player = new TurtlePlayer( world, getProfile( profile ) );
player.networkHandler = new FakeNetHandler( player );
player.setState( turtle );
if( profile != null && profile.getId() != null )
{
// Constructing a player overrides the "active player" variable in advancements. As fake players cannot
// get advancements, this prevents a normal player who has placed a turtle from getting advancements.
// We try to locate the "actual" player and restore them.
ServerPlayerEntity actualPlayer = world.getServer().getPlayerManager().getPlayer(player.getUuid());
if( actualPlayer != null ) player.getAdvancementTracker().setOwner(actualPlayer);
}
return player;
}
private static GameProfile getProfile(@Nullable GameProfile profile) {
return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE;
}
@@ -70,14 +91,17 @@ public final class TurtlePlayer extends FakePlayer {
}
public static TurtlePlayer get(ITurtleAccess access) {
if (!(access instanceof TurtleBrain)) {
return new TurtlePlayer(access);
}
ServerWorld world = (ServerWorld) access.getWorld();
if( !(access instanceof TurtleBrain) ) return create( access );
/*if (!(access instanceof TurtleBrain)) {
return new TurtlePlayer(world, access.getOwningPlayer());
}*/
TurtleBrain brain = (TurtleBrain) access;
TurtlePlayer player = brain.m_cachedPlayer;
if (player == null || player.getGameProfile() != getProfile(access.getOwningPlayer()) || player.getEntityWorld() != access.getWorld()) {
player = brain.m_cachedPlayer = new TurtlePlayer(brain);
player = brain.m_cachedPlayer = create(brain);
} else {
player.setState(access);
}

View File

@@ -1,4 +1,4 @@
View the source code at https://github.com/mystiacraft/cc-tweaked-fabric
View the source code at https://github.com/Merith-TK/cc-restiched
View the documentation at https://wiki.computercraft.cc
Visit the forum at https://forums.computercraft.cc
You can disable these messages by running "set motd.enable false"

View File

@@ -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);

View File

@@ -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`.
<table class="pretty-table">
<thead>
<tr><th colspan="8" align="center">Default Colors</th></tr>
<tr>
<th rowspan="2" align="center">Color</th>
<th colspan="3" align="center">Value</th>
<th colspan="4" align="center">Default Palette Color</th>
</tr>
<tr>
<th>Dec</th><th>Hex</th><th>Paint/Blit</th>
<th>Preview</th><th>Hex</th><th>RGB</th><th>Grayscale</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>colors.white</code></td>
<td align="right">1</td><td align="right">0x1</td><td align="right">0</td>
<td style="background:#F0F0F0"></td><td>#F0F0F0</td><td>240, 240, 240</td>
<td style="background:#F0F0F0"></td>
</tr>
<tr>
<td><code>colors.orange</code></td>
<td align="right">2</td><td align="right">0x2</td><td align="right">1</td>
<td style="background:#F2B233"></td><td>#F2B233</td><td>242, 178, 51</td>
<td style="background:#9D9D9D"></td>
</tr>
<tr>
<td><code>colors.magenta</code></td>
<td align="right">4</td><td align="right">0x4</td><td align="right">2</td>
<td style="background:#E57FD8"></td><td>#E57FD8</td><td>229, 127, 216</td>
<td style="background:#BEBEBE"></td>
</tr>
<tr>
<td><code>colors.lightBlue</code></td>
<td align="right">8</td><td align="right">0x8</td><td align="right">3</td>
<td style="background:#99B2F2"></td><td>#99B2F2</td><td>153, 178, 242</td>
<td style="background:#BFBFBF"></td>
</tr>
<tr>
<td><code>colors.yellow</code></td>
<td align="right">16</td><td align="right">0x10</td><td align="right">4</td>
<td style="background:#DEDE6C"></td><td>#DEDE6C</td><td>222, 222, 108</td>
<td style="background:#B8B8B8"></td>
</tr>
<tr>
<td><code>colors.lime</code></td>
<td align="right">32</td><td align="right">0x20</td><td align="right">5</td>
<td style="background:#7FCC19"></td><td>#7FCC19</td><td>127, 204, 25</td>
<td style="background:#767676"></td>
</tr>
<tr>
<td><code>colors.pink</code></td>
<td align="right">64</td><td align="right">0x40</td><td align="right">6</td>
<td style="background:#F2B2CC"></td><td>#F2B2CC</td><td>242, 178, 204</td>
<td style="background:#D0D0D0"></td>
</tr>
<tr>
<td><code>colors.gray</code></td>
<td align="right">128</td><td align="right">0x80</td><td align="right">7</td>
<td style="background:#4C4C4C"></td><td>#4C4C4C</td><td>76, 76, 76</td>
<td style="background:#4C4C4C"></td>
</tr>
<tr>
<td><code>colors.lightGray</code></td>
<td align="right">256</td><td align="right">0x100</td><td align="right">8</td>
<td style="background:#999999"></td><td>#999999</td><td>153, 153, 153</td>
<td style="background:#999999"></td>
</tr>
<tr>
<td><code>colors.cyan</code></td>
<td align="right">512</td><td align="right">0x200</td><td align="right">9</td>
<td style="background:#4C99B2"></td><td>#4C99B2</td><td>76, 153, 178</td>
<td style="background:#878787"></td>
</tr>
<tr>
<td><code>colors.purple</code></td>
<td align="right">1024</td><td align="right">0x400</td><td align="right">a</td>
<td style="background:#B266E5"></td><td>#B266E5</td><td>178, 102, 229</td>
<td style="background:#A9A9A9"></td>
</tr>
<tr>
<td><code>colors.blue</code></td>
<td align="right">2048</td><td align="right">0x800</td><td align="right">b</td>
<td style="background:#3366CC"></td><td>#3366CC</td><td>51, 102, 204</td>
<td style="background:#777777"></td>
</tr>
<tr>
<td><code>colors.brown</code></td>
<td align="right">4096</td><td align="right">0x1000</td><td align="right">c</td>
<td style="background:#7F664C"></td><td>#7F664C</td><td>127, 102, 76</td>
<td style="background:#656565"></td>
</tr>
<tr>
<td><code>colors.green</code></td>
<td align="right">8192</td><td align="right">0x2000</td><td align="right">d</td>
<td style="background:#57A64E"></td><td>#57A64E</td><td>87, 166, 78</td>
<td style="background:#6E6E6E"></td>
</tr>
<tr>
<td><code>colors.red</code></td>
<td align="right">16384</td><td align="right">0x4000</td><td align="right">e</td>
<td style="background:#CC4C4C"></td><td>#CC4C4C</td><td>204, 76, 76</td>
<td style="background:#767676"></td>
</tr>
<tr>
<td><code>colors.black</code></td>
<td align="right">32768</td><td align="right">0x8000</td><td align="right">f</td>
<td style="background:#111111"></td><td>#111111</td><td>17, 17, 17</td>
<td style="background:#111111"></td>
</tr>
</tbody>
</table>
@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

View File

@@ -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")

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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")

View File

@@ -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
@@ -453,13 +453,13 @@ do
error_at(pos, "Unexpected %s, expected %s.", actual, exp)
end
local function parse_string(str, pos)
local function parse_string(str, pos, terminate)
local buf, n = {}, 1
while true do
local c = sub(str, pos, pos)
if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end
if c == '"' then break end
if c == terminate then break end
if c == '\\' then
-- Handle the various escapes
@@ -485,13 +485,13 @@ do
return concat(buf, "", 1, n - 1), pos + 1
end
local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
local num_types = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
local function parse_number(str, pos, opts)
local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos)
local val = tonumber(num_str)
if not val then error_at(pos, "Malformed number %q.", num_str) end
if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end
if opts.nbt_style and num_types[sub(str, last + 1, last + 1)] then return val, last + 2 end
return val, last + 1
end
@@ -501,9 +501,11 @@ do
return val, last + 1
end
local arr_types = { I = true, L = true, B = true }
local function decode_impl(str, pos, opts)
local c = sub(str, pos, pos)
if c == '"' then return parse_string(str, pos + 1)
if c == '"' then return parse_string(str, pos + 1, '"')
elseif c == "'" and opts.nbt_style then return parse_string(str, pos + 1, "\'")
elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts)
elseif c == "t" then
if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end
@@ -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")

View File

@@ -125,7 +125,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
-- Helper functions
local function updateCursorPos()
if nCursorX >= 1 and nCursorY >= 1 and
nCursorX <= nWidth and nCursorY <= nHeight then
nCursorX <= nWidth and nCursorY <= nHeight then
parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1)
else
parent.setCursorPos(0, 0)
@@ -474,6 +474,14 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
end
end
--- Get whether this window is visible. Invisible windows will not be
-- drawn to the screen until they are made visible again.
--
-- @treturn boolean Whether this window is visible.
-- @see Window:setVisible
function window.isVisible()
return bVisible
end
--- Draw this window. This does nothing if the window is not visible.
--
-- @see Window:setVisible

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +1,5 @@
Please report bugs at https://github.com/Zundrel/cc-tweaked-fabric. Thanks!
View the documentation at https://wiki.computercraft.cc
Please report bugs at https://github.com/Merith-TK/cc-restiched. Thanks!
View the documentation at https://tweaked.cc
Show off your programs or ask for help at our forum: https://forums.computercraft.cc
You can disable these messages by running "set motd.enable false".
Use "pastebin put" to upload a program to pastebin.

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs > 2 then
print("Usage: alias <alias> <program>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <alias> <program>")
return
end

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs < 1 then
print("Usage: cd <path>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return
end

View File

@@ -4,7 +4,8 @@ if not commands then
return
end
if #tArgs == 0 then
printError("Usage: exec <command>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
printError("Usage: " .. programName .. " <command>")
return
end

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs < 2 then
print("Usage: cp <source> <destination>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return
end

View File

@@ -1,7 +1,8 @@
local args = table.pack(...)
if args.n < 1 then
print("Usage: rm <paths>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <paths>")
return
end

View File

@@ -1,7 +1,8 @@
-- Get file to edit
local tArgs = { ... }
if #tArgs == 0 then
print("Usage: edit <path>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return
end

View File

@@ -1,7 +1,8 @@
-- Get arguments
local tArgs = { ... }
if #tArgs == 0 then
print("Usage: eject <drive>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <drive>")
return
end

View File

@@ -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 <path>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return
end
local sPath = shell.resolve(tArgs[1])

View File

@@ -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 <drive>")
print("dj stop")
print(programName .. " play")
print(programName .. " play <drive>")
print(programName .. " stop")
end
if #tArgs > 2 then

View File

@@ -1,8 +1,9 @@
local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:")
print("gps host")
print("gps host <x> <y> <z>")
print("gps locate")
print(programName .. " host")
print(programName .. " host <x> <y> <z>")
print(programName .. " locate")
end
local tArgs = { ... }

View File

@@ -1,8 +1,9 @@
local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:")
print("pastebin put <filename>")
print("pastebin get <code> <filename>")
print("pastebin run <code> <arguments>")
print(programName .. " put <filename>")
print(programName .. " get <code> <filename>")
print(programName .. " run <code> <arguments>")
end
local tArgs = { ... }

View File

@@ -1,7 +1,8 @@
local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage:")
print("wget <url> [filename]")
print("wget run <url>")
print(programName .. " <url> [filename]")
print(programName .. " run <url>")
end
local tArgs = { ... }

View File

@@ -1,13 +1,15 @@
local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:")
print("label get")
print("label get <drive>")
print("label set <text>")
print("label set <drive> <text>")
print("label clear")
print("label clear <drive>")
print(programName .. " get")
print(programName .. " get <drive>")
print(programName .. " set <text>")
print(programName .. " set <drive> <text>")
print(programName .. " clear")
print(programName .. " clear <drive>")
end
local function checkDrive(sDrive)
if peripheral.getType(sDrive) == "drive" then
-- Check the disk exists

View File

@@ -1,7 +1,8 @@
local tArgs = { ... }
if #tArgs < 1 then
print("Usage: mkdir <paths>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <paths>")
return
end

View File

@@ -1,5 +1,6 @@
local function printUsage()
print("Usage: monitor <name> <program> <arguments>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <name> <program> <arguments>")
return
end

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs < 2 then
print("Usage: mv <source> <destination>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return
end

View File

@@ -1,9 +1,10 @@
local tArgs = { ... }
local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:")
print("chat host <hostname>")
print("chat join <hostname> <nickname>")
print(programName .. " host <hostname>")
print(programName .. " join <hostname> <nickname>")
end
local sOpenedModem = nil

View File

@@ -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 <side> <value>")
print("redstone set <side> <color> <value>")
print("redstone pulse <side> <count> <period>")
print(programName .. " probe")
print(programName .. " set <side> <value>")
print(programName .. " set <side> <color> <value>")
print(programName .. " pulse <side> <count> <period>")
end
local sCommand = tArgs[1]

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs < 2 then
print("Usage: rename <source> <destination>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return
end

View File

@@ -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")

View File

@@ -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

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
local function printUsage()
print("Usage: equip <slot> <side>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <slot> <side>")
end
if #tArgs ~= 2 then

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
if #tArgs ~= 1 then
print("Usage: excavate <diameter>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <diameter>")
return
end

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
if #tArgs < 1 then
print("Usage: go <direction> <distance>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <direction> <distance>")
return
end

View File

@@ -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

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
if #tArgs ~= 1 then
print("Usage: tunnel <length>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <length>")
return
end

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
if #tArgs < 1 then
print("Usage: turn <direction> <turns>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <direction> <turns>")
return
end

View File

@@ -5,7 +5,8 @@ end
local tArgs = { ... }
local function printUsage()
print("Usage: unequip <side>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <side>")
end
if #tArgs ~= 1 then

View File

@@ -1,6 +1,7 @@
local tArgs = { ... }
if #tArgs < 1 then
print("Usage: type <path>")
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return
end

View File

@@ -1,20 +1,21 @@
{
"schemaVersion": 1,
"id": "computercraft",
"name": "CC:T for Fabric",
"name": "CC:Restitched",
"version": "${version}",
"description": "A fork of CC: Tweaked for use with the latest Fabric.",
"description": "CC: Tweaked for Fabric.",
"license": "ComputerCraft Public License",
"icon": "assets/computercraft/pack.png",
"contact": {
"homepage": "https://github.com/mystiacraft/cc-tweaked-fabric",
"issues": "https://github.com/mystiacraft/cc-tweaked-fabric/issues"
"homepage": "https://github.com/Merith-TK/cc-restiched",
"issues": "https://github.com/Merith-TK/cc-restiched/issues"
},
"authors": [
"Daniel Ratcliffe",
"Aaron Mills",
"SquidDev",
"parly"
"parly",
"Merith.TK"
],
"depends": {
"fabricloader": ">=0.4.0",

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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 <command>\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)

View File

@@ -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 <source> <destination>\n", error = "" }
end)
end)

View File

@@ -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 <source> <destination>\n", error = "" }
end)
end)

View File

@@ -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)

View File

@@ -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 <slot> <side>\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)

View File

@@ -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)

View File

@@ -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 <side>\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)