diff --git a/build.gradle.kts b/build.gradle.kts index 7e0876974..42d220858 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -86,11 +86,17 @@ minecraft { val testMod = configurations["testModRuntimeClasspath"].resolve() val implementation = configurations.runtimeClasspath.get().resolve() val new = (testMod - implementation) - .asSequence().filter { it.isFile }.map { it.absolutePath } + .asSequence() + .filter { it.isFile && !it.name.endsWith("-test-fixtures.jar") } + .map { it.absolutePath } .joinToString(File.pathSeparator) if (old == null) new else old.get() + File.pathSeparator + new } + property("cctest.sources", file("src/testMod/resources/data/cctest").absolutePath) + + arg("--mixin.config=computercraft-gametest.mixins.json") + mods.register("cctest") { source(sourceSets["testMod"]) source(sourceSets["testFixtures"]) @@ -114,7 +120,6 @@ minecraft { mappings("parchment", "${libs.versions.parchmentMc.get()}-${libs.versions.parchment.get()}-$mcVersion") accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg")) - accessTransformer(file("src/testMod/resources/META-INF/accesstransformer.cfg")) } mixin { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index cee659d46..46d66d5da 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -78,7 +78,9 @@ - + + + @@ -107,7 +109,9 @@ - + + + @@ -115,11 +119,6 @@ - - - - - diff --git a/doc/stub/fs.lua b/doc/stub/fs.lua index e2d8f0741..a393d7cd2 100644 --- a/doc/stub/fs.lua +++ b/doc/stub/fs.lua @@ -22,13 +22,43 @@ directory exist) and one without it (meaning this entry is an immediate completion candidate). `include_dirs` can be set to @{false} to only include those with a trailing slash. -@tparam string path The path to complete. -@tparam string location The location where paths are resolved from. -@tparam[opt] boolean include_files When @{false}, only directories will be -included in the returned list. -@tparam[opt] boolean include_dirs When @{false}, "raw" directories will not be -included in the returned list. +@tparam[1] string path The path to complete. +@tparam[1] string location The location where paths are resolved from. +@tparam[1,opt=true] boolean include_files When @{false}, only directories will +be included in the returned list. +@tparam[1,opt=true] boolean include_dirs When @{false}, "raw" directories will +not be included in the returned list. + +@tparam[2] string path The path to complete. +@tparam[2] string location The location where paths are resolved from. +@tparam[2] { + include_dirs? = boolean, include_files? = boolean, + include_hidden? = boolean +} options +This table form is an expanded version of the previous syntax. The +`include_files` and `include_dirs` arguments from above are passed in as fields. + +This table also accepts the following options: + - `include_hidden`: Whether to include hidden files (those starting with `.`) + by default. They will still be shown when typing a `.`. + @treturn { string... } A list of possible completion candidates. @since 1.74 +@changed 1.101.0 +@usage Complete files in the root directory. + + read(nil, nil, function(str) + return fs.complete(str, "", true, false) + end) + +@usage Complete files in the root directory, hiding hidden files by default. + + read(nil, nil, function(str) + return fs.complete(str, "", { + include_files = true, + include_dirs = false, + included_hidden = false, + }) + end) ]] function complete(path, location, include_files, include_dirs) end diff --git a/gradle.properties b/gradle.properties index 1744dec7c..d7e874118 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.stdlib.default.dependency=false kotlin.jvm.target.validation.mode=error # Mod properties -modVersion=1.100.10 +modVersion=1.101.0 # Minecraft properties: We want to configure this here so we can read it in settings.gradle mcVersion=1.19.2 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 624e671c1..0ebd7d478 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ parchment = "2022.10.16" parchmentMc = "1.19.2" autoService = "1.0.1" -cobalt = { strictly = "[0.5.7,0.6.0)", prefer = "0.5.7" } +cobalt = { strictly = "[0.5.8,0.6.0)", prefer = "0.5.8" } jetbrainsAnnotations = "23.0.0" kotlin = "1.7.10" kotlin-coroutines = "1.6.0" diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 83244b562..d5c4bf679 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -15,6 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; +import java.util.OptionalInt; import java.util.concurrent.TimeUnit; @Mod( ComputerCraft.MOD_ID ) @@ -37,8 +38,8 @@ public final class ComputerCraft public static boolean httpEnabled = true; public static boolean httpWebsocketEnabled = true; public static List httpRules = List.of( - AddressRule.parse( "$private", null, Action.DENY.toPartial() ), - AddressRule.parse( "*", null, Action.ALLOW.toPartial() ) + AddressRule.parse( "$private", OptionalInt.empty(), Action.DENY.toPartial() ), + AddressRule.parse( "*", OptionalInt.empty(), Action.ALLOW.toPartial() ) ); public static int httpMaxRequests = 16; diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java index 3263e6562..6d0be7c1f 100644 --- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java +++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java @@ -22,7 +22,7 @@ import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.core.apis.ApiFactories; import dan200.computercraft.core.asm.GenericMethod; import dan200.computercraft.core.filesystem.FileMount; -import dan200.computercraft.core.filesystem.ResourceMount; +import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.shared.BundledRedstone; diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java index 8e7aa244e..687bede37 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java @@ -16,6 +16,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import java.util.Arrays; +import java.util.Collections; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -56,9 +57,8 @@ public final class ComputerSidebar () -> isOn.getAsBoolean() ? Arrays.asList( Component.translatable( "gui.computercraft.tooltip.turn_off" ), Component.translatable( "gui.computercraft.tooltip.turn_off.key" ).withStyle( ChatFormatting.GRAY ) - ) : Arrays.asList( - Component.translatable( "gui.computercraft.tooltip.turn_on" ), - Component.translatable( "gui.computercraft.tooltip.turn_off.key" ).withStyle( ChatFormatting.GRAY ) + ) : Collections.singletonList( + Component.translatable( "gui.computercraft.tooltip.turn_on" ) ) ) ); diff --git a/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java b/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java index cb3e41294..77ea455b0 100644 --- a/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java +++ b/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java @@ -8,7 +8,8 @@ package dan200.computercraft.client.pocket; import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ComputerState; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.pocket.core.PocketServerComputer; import javax.annotation.Nonnull; @@ -25,13 +26,13 @@ import javax.annotation.Nonnull; */ public class PocketComputerData { - private final Terminal terminal; + private final NetworkedTerminal terminal; private ComputerState state = ComputerState.OFF; private int lightColour = -1; public PocketComputerData( boolean colour ) { - terminal = new Terminal( ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight, colour ); + terminal = new NetworkedTerminal( ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight, colour ); } public int getLightState() diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/Action.java b/src/main/java/dan200/computercraft/core/apis/http/options/Action.java index e480d15e6..045207ae4 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/Action.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/Action.java @@ -6,13 +6,17 @@ package dan200.computercraft.core.apis.http.options; import javax.annotation.Nonnull; +import java.util.OptionalInt; +import java.util.OptionalLong; public enum Action { ALLOW, DENY; - private final PartialOptions partial = new PartialOptions( this, null, null, null, null ); + private final PartialOptions partial = new PartialOptions( + this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty() + ); @Nonnull public PartialOptions toPartial() diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java index 33fb1f250..5e54b4804 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java @@ -16,6 +16,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.OptionalInt; import java.util.regex.Pattern; /** @@ -29,10 +30,10 @@ public final class AddressRule public static final int WEBSOCKET_MESSAGE = 128 * 1024; private final AddressPredicate predicate; - private final Integer port; + private final OptionalInt port; private final PartialOptions partial; - private AddressRule( @Nonnull AddressPredicate predicate, @Nullable Integer port, @Nonnull PartialOptions partial ) + private AddressRule( @Nonnull AddressPredicate predicate, OptionalInt port, @Nonnull PartialOptions partial ) { this.predicate = predicate; this.partial = partial; @@ -40,7 +41,7 @@ public final class AddressRule } @Nullable - public static AddressRule parse( String filter, @Nullable Integer port, @Nonnull PartialOptions partial ) + public static AddressRule parse( String filter, OptionalInt port, @Nonnull PartialOptions partial ) { int cidr = filter.indexOf( '/' ); if( cidr >= 0 ) @@ -72,7 +73,7 @@ public final class AddressRule */ private boolean matches( String domain, int port, InetAddress address, Inet4Address ipv4Address ) { - if( this.port != null && this.port != port ) return false; + if( this.port.isPresent() && this.port.getAsInt() != port ) return false; return predicate.matches( domain ) || predicate.matches( address ) || (ipv4Address != null && predicate.matches( ipv4Address )); @@ -80,8 +81,7 @@ public final class AddressRule public static Options apply( Iterable rules, String domain, InetSocketAddress socketAddress ) { - PartialOptions options = null; - boolean hasMany = false; + PartialOptions options = PartialOptions.DEFAULT; int port = socketAddress.getPort(); InetAddress address = socketAddress.getAddress(); @@ -91,24 +91,9 @@ public final class AddressRule for( AddressRule rule : rules ) { if( !rule.matches( domain, port, address, ipv4Address ) ) continue; - - if( options == null ) - { - options = rule.partial; - } - else - { - - if( !hasMany ) - { - options = options.copy(); - hasMany = true; - } - - options.merge( rule.partial ); - } + options = options.merge( rule.partial ); } - return (options == null ? PartialOptions.DEFAULT : options).toOptions(); + return options.toOptions(); } } diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java index d9f0a95a5..19dd35c1c 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRuleConfig.java @@ -14,6 +14,8 @@ import dan200.computercraft.ComputerCraft; import javax.annotation.Nullable; import java.util.Locale; import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.concurrent.ConcurrentHashMap; /** @@ -48,7 +50,7 @@ public class AddressRuleConfig 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 ); + OptionalInt port = unboxOptInt( get( builder, "port", Number.class ) ); return hostObj != null && checkEnum( builder, "action", Action.class ) && check( builder, "port", Number.class ) && check( builder, "timeout", Number.class ) @@ -65,11 +67,11 @@ public class AddressRuleConfig if( hostObj == null ) return null; Action action = getEnum( builder, "action", Action.class ).orElse( null ); - Integer port = get( builder, "port", Number.class ).map( Number::intValue ).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 ); + OptionalInt port = unboxOptInt( get( builder, "port", Number.class ) ); + OptionalInt timeout = unboxOptInt( get( builder, "timeout", Number.class ) ); + OptionalLong maxUpload = unboxOptLong( get( builder, "max_upload", Number.class ).map( Number::longValue ) ); + OptionalLong maxDownload = unboxOptLong( get( builder, "max_download", Number.class ).map( Number::longValue ) ); + OptionalInt websocketMessage = unboxOptInt( get( builder, "websocket_message", Number.class ).map( Number::intValue ) ); PartialOptions options = new PartialOptions( action, @@ -122,6 +124,16 @@ public class AddressRuleConfig return get( config, field, String.class ).map( x -> parseEnum( klass, x ) ); } + private static OptionalLong unboxOptLong( Optional value ) + { + return value.map( Number::intValue ).map( OptionalLong::of ).orElse( OptionalLong.empty() ); + } + + private static OptionalInt unboxOptInt( Optional value ) + { + return value.map( Number::intValue ).map( OptionalInt::of ).orElse( OptionalInt.empty() ); + } + @Nullable private static > T parseEnum( Class klass, String x ) { diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java b/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java index efade4ec9..0ace626bb 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/PartialOptions.java @@ -5,21 +5,25 @@ */ package dan200.computercraft.core.apis.http.options; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.OptionalInt; +import java.util.OptionalLong; public final class PartialOptions { - static final PartialOptions DEFAULT = new PartialOptions( null, null, null, null, null ); + public static final PartialOptions DEFAULT = new PartialOptions( + null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty() + ); - Action action; - Long maxUpload; - Long maxDownload; - Integer timeout; - Integer websocketMessage; + private final @Nullable Action action; + private final OptionalLong maxUpload; + private final OptionalLong maxDownload; + private final OptionalInt timeout; + private final OptionalInt websocketMessage; - Options options; + private @Nullable Options options; - PartialOptions( Action action, Long maxUpload, Long maxDownload, Integer timeout, Integer websocketMessage ) + public PartialOptions( @Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt timeout, OptionalInt websocketMessage ) { this.action = action; this.maxUpload = maxUpload; @@ -28,31 +32,36 @@ public final class PartialOptions this.websocketMessage = websocketMessage; } - @Nonnull Options toOptions() { if( options != null ) return options; return options = new Options( action == null ? Action.DENY : action, - maxUpload == null ? AddressRule.MAX_UPLOAD : maxUpload, - maxDownload == null ? AddressRule.MAX_DOWNLOAD : maxDownload, - timeout == null ? AddressRule.TIMEOUT : timeout, - websocketMessage == null ? AddressRule.WEBSOCKET_MESSAGE : websocketMessage + maxUpload.orElse( AddressRule.MAX_UPLOAD ), + maxDownload.orElse( AddressRule.MAX_DOWNLOAD ), + timeout.orElse( AddressRule.TIMEOUT ), + websocketMessage.orElse( AddressRule.WEBSOCKET_MESSAGE ) ); } - void merge( @Nonnull PartialOptions other ) + /** + * Perform a left-biased union of two {@link PartialOptions}. + * + * @param other The other partial options to combine with. + * @return The merged options map. + */ + PartialOptions merge( PartialOptions other ) { - if( action == null && other.action != null ) action = other.action; - if( maxUpload == null && other.maxUpload != null ) maxUpload = other.maxUpload; - if( maxDownload == null && other.maxDownload != null ) maxDownload = other.maxDownload; - if( timeout == null && other.timeout != null ) timeout = other.timeout; - if( websocketMessage == null && other.websocketMessage != null ) websocketMessage = other.websocketMessage; - } + // Short circuit for DEFAULT. This has no effect on the outcome, but avoids an allocation. + if( this == DEFAULT ) return other; - PartialOptions copy() - { - return new PartialOptions( action, maxUpload, maxDownload, timeout, websocketMessage ); + return new PartialOptions( + action == null && other.action != null ? other.action : action, + maxUpload.isPresent() ? maxUpload : other.maxUpload, + maxDownload.isPresent() ? maxDownload : other.maxDownload, + timeout.isPresent() ? timeout : other.timeout, + websocketMessage.isPresent() ? websocketMessage : other.websocketMessage + ); } } diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/NoOriginWebSocketHanshakder.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/NoOriginWebSocketHanshakder.java new file mode 100644 index 000000000..406ab8ffc --- /dev/null +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/NoOriginWebSocketHanshakder.java @@ -0,0 +1,35 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.core.apis.http.websocket; + +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker13; +import io.netty.handler.codec.http.websocketx.WebSocketVersion; + +import java.net.URI; + +/** + * A version of {@link WebSocketClientHandshaker13} which doesn't add the {@link HttpHeaderNames#ORIGIN} header to the + * original HTTP request. + */ +public class NoOriginWebSocketHanshakder extends WebSocketClientHandshaker13 +{ + public NoOriginWebSocketHanshakder( URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength ) + { + super( webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength ); + } + + @Override + protected FullHttpRequest newHandshakeRequest() + { + FullHttpRequest request = super.newHandshakeRequest(); + HttpHeaders headers = request.headers(); + if( !customHeaders.contains( HttpHeaderNames.ORIGIN ) ) headers.remove( HttpHeaderNames.ORIGIN ); + return request; + } +} diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java index 55dd35f3f..fc352b397 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java @@ -26,7 +26,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import io.netty.handler.codec.http.websocketx.WebSocketVersion; import io.netty.handler.ssl.SslContext; @@ -152,7 +151,7 @@ public class Websocket extends Resource } String subprotocol = headers.get( HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL ); - WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( + WebSocketClientHandshaker handshaker = new NoOriginWebSocketHanshakder( uri, WebSocketVersion.V13, subprotocol, true, headers, options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage ); diff --git a/src/main/java/dan200/computercraft/core/computer/ComputerSide.java b/src/main/java/dan200/computercraft/core/computer/ComputerSide.java index 85fcfab57..897ae91b9 100644 --- a/src/main/java/dan200/computercraft/core/computer/ComputerSide.java +++ b/src/main/java/dan200/computercraft/core/computer/ComputerSide.java @@ -5,14 +5,11 @@ */ package dan200.computercraft.core.computer; -import net.minecraft.core.Direction; - import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * A side on a computer. Unlike {@link Direction}, this is relative to the direction the computer is - * facing.. + * A side on a computer. This is relative to the direction the computer is facing. */ public enum ComputerSide { diff --git a/src/main/java/dan200/computercraft/core/computer/mainthread/MainThreadExecutor.java b/src/main/java/dan200/computercraft/core/computer/mainthread/MainThreadExecutor.java index c70f98cc7..2cccf3933 100644 --- a/src/main/java/dan200/computercraft/core/computer/mainthread/MainThreadExecutor.java +++ b/src/main/java/dan200/computercraft/core/computer/mainthread/MainThreadExecutor.java @@ -10,7 +10,6 @@ import dan200.computercraft.api.peripheral.IWorkMonitor; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.metrics.Metrics; import dan200.computercraft.core.metrics.MetricsObserver; -import net.minecraft.world.level.block.entity.BlockEntity; import java.util.ArrayDeque; import java.util.Queue; @@ -28,7 +27,7 @@ import java.util.concurrent.TimeUnit; * this tick. At the beginning of the tick, we execute as many {@link MainThread} tasks as possible, until our * time-frame or the global time frame has expired. *

- * Then, when other objects (such as {@link BlockEntity}) are ticked, we update how much time we've used using + * Then, when other objects (such as block entities or entities) are ticked, we update how much time we've used via * {@link IWorkMonitor#trackWork(long, TimeUnit)}. *

* Now, if anywhere during this period, we use more than our allocated time slice, the executor is marked as diff --git a/src/main/java/dan200/computercraft/core/terminal/Terminal.java b/src/main/java/dan200/computercraft/core/terminal/Terminal.java index a5b532d95..a9271bf83 100644 --- a/src/main/java/dan200/computercraft/core/terminal/Terminal.java +++ b/src/main/java/dan200/computercraft/core/terminal/Terminal.java @@ -7,8 +7,6 @@ package dan200.computercraft.core.terminal; import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Palette; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -16,23 +14,23 @@ import java.nio.ByteBuffer; public class Terminal { - private static final String BASE_16 = "0123456789abcdef"; + protected static final String BASE_16 = "0123456789abcdef"; - private int width; - private int height; - private final boolean colour; + protected int width; + protected int height; + protected final boolean colour; - private int cursorX = 0; - private int cursorY = 0; - private boolean cursorBlink = false; - private int cursorColour = 0; - private int cursorBackgroundColour = 15; + protected int cursorX = 0; + protected int cursorY = 0; + protected boolean cursorBlink = false; + protected int cursorColour = 0; + protected int cursorBackgroundColour = 15; - private TextBuffer[] text; - private TextBuffer[] textColour; - private TextBuffer[] backgroundColour; + protected TextBuffer[] text; + protected TextBuffer[] textColour; + protected TextBuffer[] backgroundColour; - private final Palette palette; + protected final Palette palette; private final @Nullable Runnable onChanged; @@ -320,110 +318,6 @@ public class Terminal if( onChanged != null ) onChanged.run(); } - public synchronized void write( FriendlyByteBuf buffer ) - { - buffer.writeInt( cursorX ); - buffer.writeInt( cursorY ); - buffer.writeBoolean( cursorBlink ); - buffer.writeByte( cursorBackgroundColour << 4 | cursorColour ); - - for( int y = 0; y < height; y++ ) - { - TextBuffer text = this.text[y]; - TextBuffer textColour = this.textColour[y]; - TextBuffer backColour = backgroundColour[y]; - - for( int x = 0; x < width; x++ ) buffer.writeByte( text.charAt( x ) & 0xFF ); - for( int x = 0; x < width; x++ ) - { - buffer.writeByte( getColour( - backColour.charAt( x ), Colour.BLACK ) << 4 | - getColour( textColour.charAt( x ), Colour.WHITE ) - ); - } - } - - palette.write( buffer ); - } - - public synchronized void read( FriendlyByteBuf buffer ) - { - cursorX = buffer.readInt(); - cursorY = buffer.readInt(); - cursorBlink = buffer.readBoolean(); - - byte cursorColour = buffer.readByte(); - cursorBackgroundColour = (cursorColour >> 4) & 0xF; - this.cursorColour = cursorColour & 0xF; - - for( int y = 0; y < height; y++ ) - { - TextBuffer text = this.text[y]; - TextBuffer textColour = this.textColour[y]; - TextBuffer backColour = backgroundColour[y]; - - for( int x = 0; x < width; x++ ) text.setChar( x, (char) (buffer.readByte() & 0xFF) ); - for( int x = 0; x < width; x++ ) - { - byte colour = buffer.readByte(); - backColour.setChar( x, BASE_16.charAt( (colour >> 4) & 0xF ) ); - textColour.setChar( x, BASE_16.charAt( colour & 0xF ) ); - } - } - - palette.read( buffer ); - setChanged(); - } - - public synchronized CompoundTag writeToNBT( CompoundTag nbt ) - { - nbt.putInt( "term_cursorX", cursorX ); - nbt.putInt( "term_cursorY", cursorY ); - nbt.putBoolean( "term_cursorBlink", cursorBlink ); - nbt.putInt( "term_textColour", cursorColour ); - nbt.putInt( "term_bgColour", cursorBackgroundColour ); - for( int n = 0; n < height; n++ ) - { - nbt.putString( "term_text_" + n, text[n].toString() ); - nbt.putString( "term_textColour_" + n, textColour[n].toString() ); - nbt.putString( "term_textBgColour_" + n, backgroundColour[n].toString() ); - } - - palette.writeToNBT( nbt ); - return nbt; - } - - public synchronized void readFromNBT( CompoundTag nbt ) - { - cursorX = nbt.getInt( "term_cursorX" ); - cursorY = nbt.getInt( "term_cursorY" ); - cursorBlink = nbt.getBoolean( "term_cursorBlink" ); - cursorColour = nbt.getInt( "term_textColour" ); - cursorBackgroundColour = nbt.getInt( "term_bgColour" ); - - for( int n = 0; n < height; n++ ) - { - text[n].fill( ' ' ); - if( nbt.contains( "term_text_" + n ) ) - { - text[n].write( nbt.getString( "term_text_" + n ) ); - } - textColour[n].fill( BASE_16.charAt( cursorColour ) ); - if( nbt.contains( "term_textColour_" + n ) ) - { - textColour[n].write( nbt.getString( "term_textColour_" + n ) ); - } - backgroundColour[n].fill( BASE_16.charAt( cursorBackgroundColour ) ); - if( nbt.contains( "term_textBgColour_" + n ) ) - { - backgroundColour[n].write( nbt.getString( "term_textBgColour_" + n ) ); - } - } - - palette.readFromNBT( nbt ); - setChanged(); - } - public static int getColour( char c, Colour def ) { if( c >= '0' && c <= '9' ) return c - '0'; diff --git a/src/main/java/dan200/computercraft/shared/CommonHooks.java b/src/main/java/dan200/computercraft/shared/CommonHooks.java index 6a089b86c..af73d538b 100644 --- a/src/main/java/dan200/computercraft/shared/CommonHooks.java +++ b/src/main/java/dan200/computercraft/shared/CommonHooks.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared; import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.apis.http.NetworkUtils; -import dan200.computercraft.core.filesystem.ResourceMount; +import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.metrics.ComputerMBean; diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index 9dcf69357..fac637aff 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -365,8 +365,7 @@ public final class CommandComputerCraft private static final List DEFAULT_FIELDS = Arrays.asList( new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.COUNT ), new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.NONE ), - new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.AVG ), - new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.MAX ) + new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.AVG ) ); private static int displayTimings( CommandSourceStack source, AggregatedMetric sortField, List fields ) throws CommandSyntaxException diff --git a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java b/src/main/java/dan200/computercraft/shared/computer/core/ResourceMount.java similarity index 98% rename from src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java rename to src/main/java/dan200/computercraft/shared/computer/core/ResourceMount.java index 01bbac46d..852cf8766 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ResourceMount.java @@ -3,7 +3,7 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.core.filesystem; +package dan200.computercraft.shared.computer.core; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -11,6 +11,7 @@ import com.google.common.io.ByteStreams; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.core.apis.handles.ArrayByteChannel; +import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.shared.util.IoUtil; import net.minecraft.ResourceLocationException; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index 47fad09fc..0e4927d40 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -15,12 +15,12 @@ import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ComputerEnvironment; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.metrics.MetricsObserver; -import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; -import dan200.computercraft.shared.network.client.TerminalState; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; @@ -41,7 +41,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment private final MetricsObserver metrics; private final Computer computer; - private final Terminal terminal; + private final NetworkedTerminal terminal; private final AtomicBoolean terminalChanged = new AtomicBoolean( false ); private boolean changedLastFrame; @@ -54,7 +54,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment ServerContext context = ServerContext.get( level.getServer() ); instanceID = context.registry().getUnusedInstanceID(); - terminal = new Terminal( terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged ); + terminal = new NetworkedTerminal( terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged ); metrics = context.metrics().createMetricObserver( this ); computer = new Computer( context.computerContext(), this, terminal, computerID ); diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java index ee460de3d..6c738a508 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java @@ -5,13 +5,17 @@ */ package dan200.computercraft.shared.computer.core; +import com.google.common.annotations.VisibleForTesting; import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraftAPIImpl; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.core.ComputerContext; +import dan200.computercraft.core.computer.ComputerThread; import dan200.computercraft.core.computer.GlobalEnvironment; import dan200.computercraft.core.computer.mainthread.MainThread; +import dan200.computercraft.core.lua.CobaltLuaMachine; +import dan200.computercraft.core.lua.ILuaMachine; import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.computer.metrics.GlobalMetrics; import dan200.computercraft.shared.util.IDAssigner; @@ -41,6 +45,9 @@ public final class ServerContext { private static final LevelResource FOLDER = new LevelResource( ComputerCraft.MOD_ID ); + @VisibleForTesting + public static ILuaMachine.Factory luaMachine = CobaltLuaMachine::new; + private static @Nullable ServerContext instance; private final MinecraftServer server; @@ -57,7 +64,11 @@ public final class ServerContext this.server = server; storageDir = server.getWorldPath( FOLDER ); mainThread = new MainThread(); - context = new ComputerContext( new Environment( server ), ComputerCraft.computerThreads, mainThread ); + context = new ComputerContext( + new Environment( server ), + new ComputerThread( ComputerCraft.computerThreads ), + mainThread, luaMachine + ); idAssigner = new IDAssigner( storageDir.resolve( "ids.json" ) ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java index 6ea17b28b..ae7050d4c 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java @@ -11,7 +11,8 @@ import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.computer.menu.ServerInputHandler; import dan200.computercraft.shared.computer.menu.ServerInputState; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.util.SingleIntArray; import net.minecraft.world.entity.player.Player; @@ -34,7 +35,7 @@ public abstract class ContainerComputerBase extends AbstractContainerMenu implem private final @Nullable ServerComputer computer; private final @Nullable ServerInputState input; - private final @Nullable Terminal terminal; + private final @Nullable NetworkedTerminal terminal; private final ItemStack displayStack; diff --git a/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java b/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java index 0711a1bec..f2c7f829a 100644 --- a/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java +++ b/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java @@ -6,7 +6,7 @@ package dan200.computercraft.shared.computer.menu; import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.TerminalState; import net.minecraft.world.inventory.AbstractContainerMenu; /** diff --git a/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java b/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java new file mode 100644 index 000000000..83298de32 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminal.java @@ -0,0 +1,129 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.computer.terminal; + +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.core.terminal.TextBuffer; +import dan200.computercraft.shared.util.Colour; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; + +public class NetworkedTerminal extends Terminal +{ + public NetworkedTerminal( int width, int height, boolean colour ) + { + super( width, height, colour ); + } + + public NetworkedTerminal( int width, int height, boolean colour, Runnable changedCallback ) + { + super( width, height, colour, changedCallback ); + } + + public synchronized void write( FriendlyByteBuf buffer ) + { + buffer.writeInt( cursorX ); + buffer.writeInt( cursorY ); + buffer.writeBoolean( cursorBlink ); + buffer.writeByte( cursorBackgroundColour << 4 | cursorColour ); + + for( int y = 0; y < height; y++ ) + { + TextBuffer text = this.text[y]; + TextBuffer textColour = this.textColour[y]; + TextBuffer backColour = backgroundColour[y]; + + for( int x = 0; x < width; x++ ) buffer.writeByte( text.charAt( x ) & 0xFF ); + for( int x = 0; x < width; x++ ) + { + buffer.writeByte( getColour( + backColour.charAt( x ), Colour.BLACK ) << 4 | + getColour( textColour.charAt( x ), Colour.WHITE ) + ); + } + } + + palette.write( buffer ); + } + + public synchronized void read( FriendlyByteBuf buffer ) + { + cursorX = buffer.readInt(); + cursorY = buffer.readInt(); + cursorBlink = buffer.readBoolean(); + + byte cursorColour = buffer.readByte(); + cursorBackgroundColour = (cursorColour >> 4) & 0xF; + this.cursorColour = cursorColour & 0xF; + + for( int y = 0; y < height; y++ ) + { + TextBuffer text = this.text[y]; + TextBuffer textColour = this.textColour[y]; + TextBuffer backColour = backgroundColour[y]; + + for( int x = 0; x < width; x++ ) text.setChar( x, (char) (buffer.readByte() & 0xFF) ); + for( int x = 0; x < width; x++ ) + { + byte colour = buffer.readByte(); + backColour.setChar( x, BASE_16.charAt( (colour >> 4) & 0xF ) ); + textColour.setChar( x, BASE_16.charAt( colour & 0xF ) ); + } + } + + palette.read( buffer ); + setChanged(); + } + + public synchronized CompoundTag writeToNBT( CompoundTag nbt ) + { + nbt.putInt( "term_cursorX", cursorX ); + nbt.putInt( "term_cursorY", cursorY ); + nbt.putBoolean( "term_cursorBlink", cursorBlink ); + nbt.putInt( "term_textColour", cursorColour ); + nbt.putInt( "term_bgColour", cursorBackgroundColour ); + for( int n = 0; n < height; n++ ) + { + nbt.putString( "term_text_" + n, text[n].toString() ); + nbt.putString( "term_textColour_" + n, textColour[n].toString() ); + nbt.putString( "term_textBgColour_" + n, backgroundColour[n].toString() ); + } + + palette.writeToNBT( nbt ); + return nbt; + } + + public synchronized void readFromNBT( CompoundTag nbt ) + { + cursorX = nbt.getInt( "term_cursorX" ); + cursorY = nbt.getInt( "term_cursorY" ); + cursorBlink = nbt.getBoolean( "term_cursorBlink" ); + cursorColour = nbt.getInt( "term_textColour" ); + cursorBackgroundColour = nbt.getInt( "term_bgColour" ); + + for( int n = 0; n < height; n++ ) + { + text[n].fill( ' ' ); + if( nbt.contains( "term_text_" + n ) ) + { + text[n].write( nbt.getString( "term_text_" + n ) ); + } + textColour[n].fill( BASE_16.charAt( cursorColour ) ); + if( nbt.contains( "term_textColour_" + n ) ) + { + textColour[n].write( nbt.getString( "term_textColour_" + n ) ); + } + backgroundColour[n].fill( BASE_16.charAt( cursorBackgroundColour ) ); + if( nbt.contains( "term_textBgColour_" + n ) ) + { + backgroundColour[n].write( nbt.getString( "term_textBgColour_" + n ) ); + } + } + + palette.readFromNBT( nbt ); + setChanged(); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java b/src/main/java/dan200/computercraft/shared/computer/terminal/TerminalState.java similarity index 92% rename from src/main/java/dan200/computercraft/shared/network/client/TerminalState.java rename to src/main/java/dan200/computercraft/shared/computer/terminal/TerminalState.java index fc7f905d4..dd755d819 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java +++ b/src/main/java/dan200/computercraft/shared/computer/terminal/TerminalState.java @@ -3,9 +3,8 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.shared.network.client; +package dan200.computercraft.shared.computer.terminal; -import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.util.IoUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; @@ -42,12 +41,12 @@ public class TerminalState private ByteBuf compressed; - public TerminalState( @Nullable Terminal terminal ) + public TerminalState( @Nullable NetworkedTerminal terminal ) { this( terminal, true ); } - public TerminalState( @Nullable Terminal terminal, boolean compress ) + public TerminalState( @Nullable NetworkedTerminal terminal, boolean compress ) { this.compress = compress; @@ -115,17 +114,17 @@ public class TerminalState return buffer == null ? 0 : buffer.readableBytes(); } - public void apply( Terminal terminal ) + public void apply( NetworkedTerminal terminal ) { if( buffer == null ) throw new NullPointerException( "buffer" ); terminal.resize( width, height ); terminal.read( new FriendlyByteBuf( buffer ) ); } - public Terminal create() + public NetworkedTerminal create() { if( buffer == null ) throw new NullPointerException( "Terminal does not exist" ); - Terminal terminal = new Terminal( width, height, colour ); + NetworkedTerminal terminal = new NetworkedTerminal( width, height, colour ); terminal.read( new FriendlyByteBuf( buffer ) ); return terminal; } diff --git a/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java b/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java index 707f876ce..250d2892d 100644 --- a/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java +++ b/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java @@ -13,8 +13,6 @@ public enum UploadResult CONSUMED, ERROR; - public static final Component SUCCESS_TITLE = Component.translatable( "gui.computercraft.upload.success" ); - public static final Component FAILED_TITLE = Component.translatable( "gui.computercraft.upload.failed" ); public static final Component COMPUTER_OFF_MSG = Component.translatable( "gui.computercraft.upload.failed.computer_off" ); public static final Component TOO_MUCH_MSG = Component.translatable( "gui.computercraft.upload.failed.too_much" ); diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java index bdcedb696..8491ab44e 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared.network.client; import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.network.NetworkMessage; import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java index 5aff75662..20fd67b38 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.shared.network.client; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java index 8e5e8b01b..650af2f72 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java @@ -7,8 +7,9 @@ package dan200.computercraft.shared.network.client; import dan200.computercraft.client.pocket.ClientPocketComputers; import dan200.computercraft.client.pocket.PocketComputerData; -import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ComputerState; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.pocket.core.PocketServerComputer; import net.minecraft.network.FriendlyByteBuf; @@ -29,7 +30,7 @@ public class PocketComputerDataMessage implements NetworkMessage instanceId = computer.getInstanceID(); state = computer.getState(); lightState = computer.getLight(); - terminal = sendTerminal ? computer.getTerminalState() : new TerminalState( (Terminal) null ); + terminal = sendTerminal ? computer.getTerminalState() : new TerminalState( (NetworkedTerminal) null ); } public PocketComputerDataMessage( FriendlyByteBuf buf ) diff --git a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java index 56499ce8e..3c55bbf8b 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.network.container; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.TerminalState; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java index e320f514e..71d48e44a 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/EnergyMethods.java @@ -25,6 +25,7 @@ import javax.annotation.Nonnull; * ::: * * @cc.module energy_storage + * @cc.since 1.94.0 */ public class EnergyMethods implements GenericPeripheral { 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 73d56246c..ba2790646 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 @@ -35,6 +35,7 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel * Methods for interacting with tanks and other fluid storage blocks. * * @cc.module fluid_storage + * @cc.since 1.94.0 */ public class FluidMethods implements GenericPeripheral { 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 649fbbc4e..7111cb44e 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 @@ -37,6 +37,7 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel * Methods for interacting with inventories. * * @cc.module inventory + * @cc.since 1.94.0 */ public class InventoryMethods implements GenericPeripheral { @@ -170,6 +171,7 @@ public class InventoryMethods implements GenericPeripheral * end * print(total) * } + * @cc.since 1.96.0 */ @LuaFunction( mainThread = true ) public static int getItemLimit( IItemHandler inventory, int slot ) throws LuaException diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java index 1437bad99..3bf0a3e55 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -9,7 +9,8 @@ import com.mojang.blaze3d.platform.GlStateManager; import dan200.computercraft.client.util.DirectBuffers; import dan200.computercraft.client.util.DirectVertexBuffer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; import net.minecraft.core.BlockPos; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -36,7 +37,7 @@ public final class ClientMonitor public int tboUniform; public DirectVertexBuffer backgroundBuffer; public DirectVertexBuffer foregroundBuffer; - private Terminal terminal; + private NetworkedTerminal terminal; private boolean terminalChanged; public ClientMonitor( TileMonitor origin ) @@ -182,7 +183,7 @@ public final class ClientMonitor { if( state.hasTerminal() ) { - if( terminal == null ) terminal = new Terminal( state.width, state.height, state.colour ); + if( terminal == null ) terminal = new NetworkedTerminal( state.width, state.height, state.colour ); state.apply( terminal ); terminalChanged = true; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java index dc389f0b7..3e7d5f8a4 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java @@ -8,7 +8,7 @@ package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.client.MonitorClientMessage; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.TerminalState; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java index 2e7efc0de..8a75a3a56 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; import com.google.common.annotations.VisibleForTesting; import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.util.TickScheduler; import javax.annotation.Nullable; @@ -18,7 +19,7 @@ public class ServerMonitor private final boolean colour; private int textScale = 2; - private @Nullable Terminal terminal; + private @Nullable NetworkedTerminal terminal; private final AtomicBoolean resized = new AtomicBoolean( false ); private final AtomicBoolean changed = new AtomicBoolean( false ); @@ -46,7 +47,7 @@ public class ServerMonitor if( terminal == null ) { - terminal = new Terminal( termWidth, termHeight, colour, this::markChanged ); + terminal = new NetworkedTerminal( termWidth, termHeight, colour, this::markChanged ); markChanged(); } else @@ -91,7 +92,7 @@ public class ServerMonitor @Nullable @VisibleForTesting - public Terminal getTerminal() + public NetworkedTerminal getTerminal() { return terminal; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java index b89b699ec..cd0874425 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java @@ -11,7 +11,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.common.TileGeneric; -import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.util.CapabilityUtil; import dan200.computercraft.shared.util.TickScheduler; import net.minecraft.core.BlockPos; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java index a45e9edcd..9d3c57157 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java @@ -8,6 +8,7 @@ package dan200.computercraft.shared.peripheral.printer; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.common.TileGeneric; +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.util.*; import net.minecraft.core.BlockPos; @@ -62,7 +63,7 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent SidedCaps.ofNullable( facing -> facing == null ? new InvWrapper( this ) : new SidedInvWrapper( this, facing ) ); private LazyOptional peripheralCap; - private final Terminal page = new Terminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE, true ); + private final NetworkedTerminal page = new NetworkedTerminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE, true ); private String pageTitle = ""; private boolean printing = false; diff --git a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java index 2896f285c..c4180e620 100644 --- a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -177,7 +178,7 @@ public final class NBTUtil { MessageDigest digest = MessageDigest.getInstance( "MD5" ); DataOutput output = new DataOutputStream( new DigestOutputStream( digest ) ); - NbtIo.write( tag, output ); + writeTag( output, "", tag ); byte[] hash = digest.digest(); return ENCODING.encode( hash ); } @@ -188,6 +189,37 @@ public final class NBTUtil } } + /** + * An alternative version of {@link NbtIo#write(CompoundTag, DataOutput)}, which sorts keys. This + * should make the output slightly more deterministic. + * + * @param output The output to write to. + * @param name The name of the key we're writing. Should be {@code ""} for the root node. + * @param tag The tag to write. + * @throws IOException If the underlying stream throws. + * @see NbtIo#write(CompoundTag, DataOutput) + * @see CompoundTag#write(DataOutput) + */ + private static void writeTag( DataOutput output, String name, Tag tag ) throws IOException + { + output.writeByte( tag.getId() ); + if( tag.getId() == 0 ) return; + output.writeUTF( name ); + + if( tag instanceof CompoundTag compound ) + { + String[] keys = compound.getAllKeys().toArray( new String[0] ); + Arrays.sort( keys ); + for( String key : keys ) writeTag( output, key, compound.get( key ) ); + + output.writeByte( 0 ); + } + else + { + tag.write( output ); + } + } + private static final class DigestOutputStream extends OutputStream { private final MessageDigest digest; diff --git a/src/main/resources/assets/computercraft/lang/en_us.json b/src/main/resources/assets/computercraft/lang/en_us.json index 33de587af..ed420e7a0 100644 --- a/src/main/resources/assets/computercraft/lang/en_us.json +++ b/src/main/resources/assets/computercraft/lang/en_us.json @@ -111,13 +111,10 @@ "gui.computercraft.tooltip.computer_id": "Computer ID: %s", "gui.computercraft.tooltip.disk_id": "Disk ID: %s", "gui.computercraft.tooltip.turn_on": "Turn this computer on", - "gui.computercraft.tooltip.turn_on.key": "Hold Ctrl+R", "gui.computercraft.tooltip.turn_off": "Turn this computer off", "gui.computercraft.tooltip.turn_off.key": "Hold Ctrl+S", "gui.computercraft.tooltip.terminate": "Stop the currently running code", "gui.computercraft.tooltip.terminate.key": "Hold Ctrl+T", - "gui.computercraft.upload.success": "Upload Succeeded", - "gui.computercraft.upload.success.msg": "%d files uploaded.", "gui.computercraft.upload.failed": "Upload Failed", "gui.computercraft.upload.failed.computer_off": "You must turn the computer on before uploading files.", "gui.computercraft.upload.failed.too_much": "Your files are too large to be uploaded.", diff --git a/src/main/resources/data/computercraft/lua/bios.lua b/src/main/resources/data/computercraft/lua/bios.lua index d31e20335..a653c97a6 100644 --- a/src/main/resources/data/computercraft/lua/bios.lua +++ b/src/main/resources/data/computercraft/lua/bios.lua @@ -3,7 +3,7 @@ -- Ideally we'd use require, but that is part of the shell, and so is not -- available to the BIOS or any APIs. All APIs load this using dofile, but that -- has not been defined at this point. -local expect +local expect, field do local h = fs.open("rom/modules/main/cc/expect.lua", "r") @@ -11,7 +11,8 @@ do h.close() if not f then error(err) end - expect = f().expect + local res = f() + expect, field = res.expect, res.field end if _VERSION == "Lua 5.1" then @@ -716,9 +717,17 @@ local tEmpty = {} function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs) expect(1, sPath, "string") expect(2, sLocation, "string") - expect(3, bIncludeFiles, "boolean", "nil") - expect(4, bIncludeDirs, "boolean", "nil") + local bIncludeHidden = nil + if type(bIncludeFiles) == "table" then + bIncludeDirs = field(bIncludeFiles, "include_dirs", "boolean", "nil") + bIncludeHidden = field(bIncludeFiles, "include_hidden", "boolean", "nil") + bIncludeFiles = field(bIncludeFiles, "include_files", "boolean", "nil") + else + expect(3, bIncludeFiles, "boolean", "nil") + expect(4, bIncludeDirs, "boolean", "nil") + end + bIncludeHidden = bIncludeHidden ~= false bIncludeFiles = bIncludeFiles ~= false bIncludeDirs = bIncludeDirs ~= false local sDir = sLocation @@ -755,7 +764,9 @@ function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs) local tFiles = fs.list(sDir) for n = 1, #tFiles do local sFile = tFiles[n] - if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName then + if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName and ( + bIncludeHidden or sFile:sub(1, 1) ~= "." or sName:sub(1, 1) == "." + ) then local bIsDir = fs.isDir(fs.combine(sDir, sFile)) local sResult = string.sub(sFile, #sName + 1) if bIsDir then @@ -902,7 +913,7 @@ settings.define("paint.default_extension", { settings.define("list.show_hidden", { default = false, - description = [[Show hidden files (those starting with "." in the Lua REPL)]], + description = [[Show hidden files (those starting with "." in the Lua REPL).]], type = "boolean", }) @@ -937,6 +948,11 @@ settings.define("bios.strict_globals", { description = "Prevents assigning variables into a program's environment. Make sure you use the local keyword or assign to _G explicitly.", type = "boolean", }) +settings.define("shell.autocomplete_hidden", { + default = false, + description = [[Autocomplete hidden files and folders (those starting with ".").]], + type = "boolean", +}) if term.isColour() then settings.define("bios.use_multishell", { diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/src/main/resources/data/computercraft/lua/rom/help/changelog.md index 50c78ded4..a2691c8b6 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,10 +1,28 @@ +# New features in CC: Tweaked 1.101.0 + +* Improvee Dutch translation (Quezler) +* Better reporting of fatal computer timeouts in the server log. +* Convert detail providers into a registry, allowing peripheral mods to read item/block details. +* Redesign the metrics system. `/computercraft track` now allows computing aggregates (total, max, avg) on any metric, not just computer time. +* File drag-and-drop now queues a `file_transfer` event on the computer. The + built-in shell or the `import` program must now be running to upload files. +* The `peripheral` now searches for remote peripherals using any peripheral with the `peripheral_hub` type, not just wired modems. +* Add `include_hidden` option to `fs.complete`, which can be used to prevent hidden files showing up in autocomplete results. (IvoLeal72) +* Add `shell.autocomplete_hidden` setting. (IvoLeal72) + +Several bug fixes: +* Prevent `edit`'s "Run" command scrolling the terminal output on smaller + screens. +* Remove some non-determinism in computing item's `nbt` hash. +* Don't set the `Origin` header on outgoing websocket requests. + # New features in CC: Tweaked 1.100.10 * Mention WAV support in speaker help (MCJack123). * Add http programs to the path, even when http is not enabled. Several bug fixes: -* Fix example in textutils.pagedTabulate docs (IvoLeal72). +* Fix example in `textutils.pagedTabulate` docs (IvoLeal72). * Fix help program treating the terminal one line longer than it was. * Send block updates to client when turtle moves (roland-a). * Resolve several monitor issues when running Occulus shaders. @@ -236,7 +254,7 @@ And several bug fixes: # New features in CC: Tweaked 1.96.0 * Use lightGrey for folders within the "list" program. -* Add getLimit to inventory peripherals. +* Add `getItemLimit` to inventory peripherals. * Expose the generic peripheral system to the public API. * Add cc.expect.range (Lupus590). * Allow calling cc.expect directly (MCJack123). diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index 50ad6318a..874b04d8a 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,12 +1,19 @@ -New features in CC: Tweaked 1.100.10 +New features in CC: Tweaked 1.101.0 -* Mention WAV support in speaker help (MCJack123). -* Add http programs to the path, even when http is not enabled. +* Improvee Dutch translation (Quezler) +* Better reporting of fatal computer timeouts in the server log. +* Convert detail providers into a registry, allowing peripheral mods to read item/block details. +* Redesign the metrics system. `/computercraft track` now allows computing aggregates (total, max, avg) on any metric, not just computer time. +* File drag-and-drop now queues a `file_transfer` event on the computer. The + built-in shell or the `import` program must now be running to upload files. +* The `peripheral` now searches for remote peripherals using any peripheral with the `peripheral_hub` type, not just wired modems. +* Add `include_hidden` option to `fs.complete`, which can be used to prevent hidden files showing up in autocomplete results. (IvoLeal72) +* Add `shell.autocomplete_hidden` setting. (IvoLeal72) Several bug fixes: -* Fix example in textutils.pagedTabulate docs (IvoLeal72). -* Fix help program treating the terminal one line longer than it was. -* Send block updates to client when turtle moves (roland-a). -* Resolve several monitor issues when running Occulus shaders. +* Prevent `edit`'s "Run" command scrolling the terminal output on smaller + screens. +* Remove some non-determinism in computing item's `nbt` hash. +* Don't set the `Origin` header on outgoing websocket requests. Type "help changelog" to see the full version history. diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/audio/dfpwm.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/audio/dfpwm.lua index fd8fa2686..fc8e8fa66 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/audio/dfpwm.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/audio/dfpwm.lua @@ -27,9 +27,9 @@ application or development builds of [FFmpeg]. @see guide!speaker_audio Gives a more general introduction to audio processing and the speaker. @see speaker.playAudio To play the decoded audio data. +@since 1.100.0 @usage Reads "data/example.dfpwm" in chunks, decodes them and then doubles the speed of the audio. The resulting audio is then re-encoded and saved to "speedy.dfpwm". This processed audio can then be played with the `speaker` program. -@since 1.100.0 ```lua local dfpwm = require("cc.audio.dfpwm") diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua index 4bdc24674..86a061c42 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -34,7 +34,11 @@ local completion = require "cc.completion" -- @tparam string text Current text to complete. -- @treturn { string... } A list of suffixes of matching files. local function file(shell, text) - return fs.complete(text, shell.dir(), true, false) + return fs.complete(text, shell.dir(), { + include_files = true, + include_dirs = false, + include_hidden = settings.get("shell.autocomplete_hidden"), + }) end --- Complete the name of a directory relative to the current working directory. @@ -43,7 +47,11 @@ end -- @tparam string text Current text to complete. -- @treturn { string... } A list of suffixes of matching directories. local function dir(shell, text) - return fs.complete(text, shell.dir(), false, true) + return fs.complete(text, shell.dir(), { + include_files = false, + include_dirs = true, + include_hidden = settings.get("shell.autocomplete_hidden"), + }) end --- Complete the name of a file or directory relative to the current working @@ -55,7 +63,11 @@ end -- @tparam[opt] boolean add_space Whether to add a space after the completed item. -- @treturn { string... } A list of suffixes of matching files and directories. local function dirOrFile(shell, text, previous, add_space) - local results = fs.complete(text, shell.dir(), true, true) + local results = fs.complete(text, shell.dir(), { + include_files = true, + include_dirs = true, + include_hidden = settings.get("shell.autocomplete_hidden"), + }) if add_space then for n = 1, #results do local result = results[n] 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 5331854d9..dd9b9f282 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua @@ -55,19 +55,22 @@ term.redirect(current) term.setTextColor(term.isColour() and colours.yellow or colours.white) term.setBackgroundColor(colours.black) term.setCursorBlink(false) -local _, y = term.getCursorPos() -local _, h = term.getSize() if not ok then printError(err) end -if ok and y >= h then - term.scroll(1) + +local message = "Press any key to continue." +if ok then message = "Program finished. " .. message end +local _, y = term.getCursorPos() +local w, h = term.getSize() +local wrapped = require("cc.strings").wrap(message, w) + +local start_y = h - #wrapped + 1 +if y >= start_y then term.scroll(y - start_y + 1) end +for i = 1, #wrapped do + term.setCursorPos(1, start_y + i - 1) + term.write(wrapped[i]) end -term.setCursorPos(1, h) -if ok then - write("Program finished. ") -end -write("Press any key to continue") os.pullEvent('key') ]] diff --git a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua index f2b650d03..6add0424f 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua @@ -329,9 +329,14 @@ function shell.programs(include_hidden) end local function completeProgram(sLine) + local bIncludeHidden = settings.get("shell.autocomplete_hidden") if #sLine > 0 and (sLine:find("/") or sLine:find("\\")) then -- Add programs from the root - return fs.complete(sLine, sDir, true, false) + return fs.complete(sLine, sDir, { + include_files = true, + include_dirs = false, + include_hidden = bIncludeHidden, + }) else local tResults = {} @@ -349,7 +354,11 @@ local function completeProgram(sLine) end -- Add all subdirectories. We don't include files as they will be added in the block below - local tDirs = fs.complete(sLine, sDir, false, false) + local tDirs = fs.complete(sLine, sDir, { + include_files = false, + include_dirs = false, + include_hidden = bIncludeHidden, + }) for i = 1, #tDirs do local sResult = tDirs[i] if not tSeen[sResult] then diff --git a/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java index 77701b5d1..a57cc9a3d 100644 --- a/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java +++ b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.params.provider.ValueSource; import java.net.InetSocketAddress; import java.util.Collections; +import java.util.OptionalInt; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -21,8 +22,8 @@ public class AddressRuleTest public void matchesPort() { Iterable rules = Collections.singletonList( AddressRule.parse( - "127.0.0.1", 8080, - new PartialOptions( Action.ALLOW, null, null, null, null ) + "127.0.0.1", OptionalInt.of( 8080 ), + Action.ALLOW.toPartial() ) ); assertEquals( apply( rules, "localhost", 8080 ).action, Action.ALLOW ); diff --git a/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java b/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java index fe495a7b4..ddcd50db9 100644 --- a/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java +++ b/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java @@ -9,9 +9,6 @@ import dan200.computercraft.api.lua.LuaValues; import dan200.computercraft.shared.util.Colour; import dan200.computercraft.test.core.CallCounter; import dan200.computercraft.test.core.terminal.TerminalMatchers; -import io.netty.buffer.Unpooled; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import org.hamcrest.Matcher; import org.junit.jupiter.api.Test; @@ -597,92 +594,6 @@ class TerminalTest callCounter.assertNotCalled(); } - @Test - void testPacketBufferRoundtrip() - { - Terminal writeTerminal = new Terminal( 2, 1, true ); - - blit( writeTerminal, "hi", "11", "ee" ); - writeTerminal.setCursorPos( 2, 5 ); - writeTerminal.setTextColour( 3 ); - writeTerminal.setBackgroundColour( 5 ); - - FriendlyByteBuf packetBuffer = new FriendlyByteBuf( Unpooled.buffer() ); - writeTerminal.write( packetBuffer ); - - CallCounter callCounter = new CallCounter(); - Terminal readTerminal = new Terminal( 2, 1, true, callCounter ); - packetBuffer.writeBytes( packetBuffer ); - readTerminal.read( packetBuffer ); - - assertThat( readTerminal, allOf( - textMatches( new String[] { "hi", } ), - textColourMatches( new String[] { "11", } ), - backgroundColourMatches( new String[] { "ee", } ) - ) ); - - assertEquals( 2, readTerminal.getCursorX() ); - assertEquals( 5, readTerminal.getCursorY() ); - assertEquals( 3, readTerminal.getTextColour() ); - assertEquals( 5, readTerminal.getBackgroundColour() ); - callCounter.assertCalledTimes( 1 ); - } - - @Test - void testNbtRoundtrip() - { - Terminal writeTerminal = new Terminal( 10, 5, true ); - blit( writeTerminal, "hi", "11", "ee" ); - writeTerminal.setCursorPos( 2, 5 ); - writeTerminal.setTextColour( 3 ); - writeTerminal.setBackgroundColour( 5 ); - - CompoundTag nbt = new CompoundTag(); - writeTerminal.writeToNBT( nbt ); - - CallCounter callCounter = new CallCounter(); - Terminal readTerminal = new Terminal( 2, 1, true, callCounter ); - - readTerminal.readFromNBT( nbt ); - - assertThat( readTerminal, allOf( - textMatches( new String[] { "hi", } ), - textColourMatches( new String[] { "11", } ), - backgroundColourMatches( new String[] { "ee", } ) - ) ); - - assertEquals( 2, readTerminal.getCursorX() ); - assertEquals( 5, readTerminal.getCursorY() ); - assertEquals( 3, readTerminal.getTextColour() ); - assertEquals( 5, readTerminal.getBackgroundColour() ); - callCounter.assertCalledTimes( 1 ); - } - - @Test - void testReadWriteNBTEmpty() - { - Terminal terminal = new Terminal( 0, 0, true ); - - CompoundTag nbt = new CompoundTag(); - terminal.writeToNBT( nbt ); - - CallCounter callCounter = new CallCounter(); - terminal = new Terminal( 0, 1, true, callCounter ); - terminal.readFromNBT( nbt ); - - assertThat( terminal, allOf( - textMatches( new String[] { "", } ), - textColourMatches( new String[] { "", } ), - backgroundColourMatches( new String[] { "", } ) - ) ); - - assertEquals( 0, terminal.getCursorX() ); - assertEquals( 0, terminal.getCursorY() ); - assertEquals( 0, terminal.getTextColour() ); - assertEquals( 15, terminal.getBackgroundColour() ); - callCounter.assertCalledTimes( 1 ); - } - @Test void testGetColour() { diff --git a/src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java b/src/test/java/dan200/computercraft/shared/computer/core/ResourceMountTest.java similarity index 97% rename from src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java rename to src/test/java/dan200/computercraft/shared/computer/core/ResourceMountTest.java index daf93b599..e88632472 100644 --- a/src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java +++ b/src/test/java/dan200/computercraft/shared/computer/core/ResourceMountTest.java @@ -3,7 +3,7 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.core.filesystem; +package dan200.computercraft.shared.computer.core; import dan200.computercraft.api.filesystem.IMount; import net.minecraft.Util; diff --git a/src/test/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminalTest.java b/src/test/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminalTest.java new file mode 100644 index 000000000..8614b67c0 --- /dev/null +++ b/src/test/java/dan200/computercraft/shared/computer/terminal/NetworkedTerminalTest.java @@ -0,0 +1,113 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.computer.terminal; + +import dan200.computercraft.api.lua.LuaValues; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.test.core.CallCounter; +import io.netty.buffer.Unpooled; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import org.junit.jupiter.api.Test; + +import static dan200.computercraft.test.core.terminal.TerminalMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class NetworkedTerminalTest +{ + @Test + void testPacketBufferRoundtrip() + { + var writeTerminal = new NetworkedTerminal( 2, 1, true ); + + blit( writeTerminal, "hi", "11", "ee" ); + writeTerminal.setCursorPos( 2, 5 ); + writeTerminal.setTextColour( 3 ); + writeTerminal.setBackgroundColour( 5 ); + + FriendlyByteBuf packetBuffer = new FriendlyByteBuf( Unpooled.buffer() ); + writeTerminal.write( packetBuffer ); + + CallCounter callCounter = new CallCounter(); + var readTerminal = new NetworkedTerminal( 2, 1, true, callCounter ); + packetBuffer.writeBytes( packetBuffer ); + readTerminal.read( packetBuffer ); + + assertThat( readTerminal, allOf( + textMatches( new String[] { "hi", } ), + textColourMatches( new String[] { "11", } ), + backgroundColourMatches( new String[] { "ee", } ) + ) ); + + assertEquals( 2, readTerminal.getCursorX() ); + assertEquals( 5, readTerminal.getCursorY() ); + assertEquals( 3, readTerminal.getTextColour() ); + assertEquals( 5, readTerminal.getBackgroundColour() ); + callCounter.assertCalledTimes( 1 ); + } + + @Test + void testNbtRoundtrip() + { + var writeTerminal = new NetworkedTerminal( 10, 5, true ); + blit( writeTerminal, "hi", "11", "ee" ); + writeTerminal.setCursorPos( 2, 5 ); + writeTerminal.setTextColour( 3 ); + writeTerminal.setBackgroundColour( 5 ); + + CompoundTag nbt = new CompoundTag(); + writeTerminal.writeToNBT( nbt ); + + CallCounter callCounter = new CallCounter(); + var readTerminal = new NetworkedTerminal( 2, 1, true, callCounter ); + + readTerminal.readFromNBT( nbt ); + + assertThat( readTerminal, allOf( + textMatches( new String[] { "hi", } ), + textColourMatches( new String[] { "11", } ), + backgroundColourMatches( new String[] { "ee", } ) + ) ); + + assertEquals( 2, readTerminal.getCursorX() ); + assertEquals( 5, readTerminal.getCursorY() ); + assertEquals( 3, readTerminal.getTextColour() ); + assertEquals( 5, readTerminal.getBackgroundColour() ); + callCounter.assertCalledTimes( 1 ); + } + + @Test + void testReadWriteNBTEmpty() + { + var terminal = new NetworkedTerminal( 0, 0, true ); + + CompoundTag nbt = new CompoundTag(); + terminal.writeToNBT( nbt ); + + CallCounter callCounter = new CallCounter(); + terminal = new NetworkedTerminal( 0, 1, true, callCounter ); + terminal.readFromNBT( nbt ); + + assertThat( terminal, allOf( + textMatches( new String[] { "", } ), + textColourMatches( new String[] { "", } ), + backgroundColourMatches( new String[] { "", } ) + ) ); + + assertEquals( 0, terminal.getCursorX() ); + assertEquals( 0, terminal.getCursorY() ); + assertEquals( 0, terminal.getTextColour() ); + assertEquals( 15, terminal.getBackgroundColour() ); + callCounter.assertCalledTimes( 1 ); + } + + private static void blit( Terminal terminal, String text, String fg, String bg ) + { + terminal.blit( LuaValues.encode( text ), LuaValues.encode( fg ), LuaValues.encode( bg ) ); + } +} diff --git a/src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java b/src/test/java/dan200/computercraft/shared/computer/terminal/TerminalStateTest.java similarity index 84% rename from src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java rename to src/test/java/dan200/computercraft/shared/computer/terminal/TerminalStateTest.java index 01a4bd325..3b99ec573 100644 --- a/src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java +++ b/src/test/java/dan200/computercraft/shared/computer/terminal/TerminalStateTest.java @@ -3,7 +3,7 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.shared.network.client; +package dan200.computercraft.shared.computer.terminal; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; @@ -23,7 +23,7 @@ public class TerminalStateTest @RepeatedTest( 5 ) public void testCompressed() { - Terminal terminal = randomTerminal(); + var terminal = randomTerminal(); FriendlyByteBuf buffer = new FriendlyByteBuf( Unpooled.directBuffer() ); new TerminalState( terminal, true ).write( buffer ); @@ -35,7 +35,7 @@ public class TerminalStateTest @RepeatedTest( 5 ) public void testUncompressed() { - Terminal terminal = randomTerminal(); + var terminal = randomTerminal(); FriendlyByteBuf buffer = new FriendlyByteBuf( Unpooled.directBuffer() ); new TerminalState( terminal, false ).write( buffer ); @@ -44,10 +44,10 @@ public class TerminalStateTest assertEquals( 0, buffer.readableBytes() ); } - private static Terminal randomTerminal() + private static NetworkedTerminal randomTerminal() { Random random = new Random(); - Terminal terminal = new Terminal( 10, 5, true ); + NetworkedTerminal terminal = new NetworkedTerminal( 10, 5, true ); for( int y = 0; y < terminal.getHeight(); y++ ) { TextBuffer buffer = terminal.getLine( y ); @@ -70,14 +70,14 @@ public class TerminalStateTest } } - private static Terminal read( FriendlyByteBuf buffer ) + private static NetworkedTerminal read( FriendlyByteBuf buffer ) { TerminalState state = new TerminalState( buffer ); assertTrue( state.colour ); if( !state.hasTerminal() ) return null; - Terminal other = new Terminal( state.width, state.height, true ); + var other = new NetworkedTerminal( state.width, state.height, true ); state.apply( other ); return other; } diff --git a/src/test/resources/test-rom/spec/apis/fs_spec.lua b/src/test/resources/test-rom/spec/apis/fs_spec.lua index 32503598e..7bcab9e86 100644 --- a/src/test/resources/test-rom/spec/apis/fs_spec.lua +++ b/src/test/resources/test-rom/spec/apis/fs_spec.lua @@ -10,6 +10,46 @@ describe("The fs library", function() expect.error(fs.complete, "", "", 1):eq("bad argument #3 (expected boolean, got number)") expect.error(fs.complete, "", "", true, 1):eq("bad argument #4 (expected boolean, got number)") end) + + describe("include_hidden", function() + local dir = "tmp/hidden" + local function setup_tree() + fs.delete(dir) + fs.makeDir(dir) + fs.open(dir .. "/file.txt", "w").close() + fs.open(dir .. "/.hidden.txt", "w").close() + end + + it("hides hidden files", function() + setup_tree() + local opts = { include_files = true, include_dirs = false, include_hidden = false } + + expect(fs.complete("", dir, opts)):same { "../", "file.txt" } + expect(fs.complete(dir .. "/", "", opts)):same { "file.txt" } + end) + + it("shows hidden files when typing a dot", function() + setup_tree() + local opts = { include_files = true, include_dirs = false, include_hidden = false } + + expect(fs.complete(".", dir, opts)):same { "./", "hidden.txt" } + expect(fs.complete(dir .. "/.", "", opts)):same { "hidden.txt" } + + -- Also test + expect(fs.complete(dir .. "/file", "", opts)):same { ".txt" } + expect(fs.complete(dir .. "/file.", "", opts)):same { "txt" } + expect(fs.complete("file", dir, opts)):same { ".txt" } + expect(fs.complete("file.", dir, opts)):same { "txt" } + end) + + it("shows hidden files when include_hidden is true", function() + setup_tree() + local opts = { include_files = true, include_dirs = false, include_hidden = true } + + expect(fs.complete("", dir, opts)):same { "../", ".hidden.txt", "file.txt" } + expect(fs.complete(dir .. "/", "", opts)):same { ".hidden.txt", "file.txt" } + end) + end) end) describe("fs.isDriveRoot", function() diff --git a/src/test/resources/test-rom/spec/apis/os_spec.lua b/src/test/resources/test-rom/spec/apis/os_spec.lua index 6fd7838b3..a9a620d50 100644 --- a/src/test/resources/test-rom/spec/apis/os_spec.lua +++ b/src/test/resources/test-rom/spec/apis/os_spec.lua @@ -61,9 +61,9 @@ describe("The os library", function() -- TODO: Java 16 apparently no longer treats TextStyle.FULL as full and will render Sun instead of Sunday. exp_code("%a", "Sun") - -- exp_code("%A", "Sunday") + exp_code("%A", "Sunday") exp_code("%b", "Oct") - -- exp_code("%B", "October") + exp_code("%B", "October") exp_code("%c", "Sun Oct 1 23:12:17 2000") exp_code("%C", "20") exp_code("%d", "01") diff --git a/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/KotlinLuaMachine.kt b/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/KotlinLuaMachine.kt index ad36ae258..3e94dea31 100644 --- a/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/KotlinLuaMachine.kt +++ b/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/KotlinLuaMachine.kt @@ -5,11 +5,9 @@ import dan200.computercraft.api.lua.ILuaContext import dan200.computercraft.core.lua.ILuaMachine import dan200.computercraft.core.lua.MachineEnvironment import dan200.computercraft.core.lua.MachineResult -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import java.io.InputStream +import kotlin.coroutines.CoroutineContext /** * An [ILuaMachine] which runs Kotlin functions instead. @@ -26,7 +24,7 @@ abstract class KotlinLuaMachine(environment: MachineEnvironment) : ILuaMachine, queueEvent(eventName, arguments) } else { val task = getTask() - if (task != null) CoroutineScope(Dispatchers.Unconfined + CoroutineName("Computer")).launch { task() } + if (task != null) CoroutineScope(NeverDispatcher() + CoroutineName("Computer")).launch { task() } } return MachineResult.OK @@ -38,4 +36,20 @@ abstract class KotlinLuaMachine(environment: MachineEnvironment) : ILuaMachine, * Get the next task to execute on this computer. */ protected abstract fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? + + /** + * A [CoroutineDispatcher] which only allows resuming from the computer thread. In practice, this means the only + * way to yield is with [pullEvent]. + */ + private class NeverDispatcher : CoroutineDispatcher() { + private val expectedGroup = Thread.currentThread().threadGroup + + override fun dispatch(context: CoroutineContext, block: Runnable) { + if (Thread.currentThread().threadGroup != expectedGroup) { + throw UnsupportedOperationException("Cannot perform arbitrary yields") + } + + block.run() + } + } } diff --git a/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/LuaTaskContext.kt b/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/LuaTaskContext.kt index 28ab11c3a..066045b87 100644 --- a/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/LuaTaskContext.kt +++ b/src/testFixtures/kotlin/dan200/computercraft/test/core/computer/LuaTaskContext.kt @@ -4,9 +4,11 @@ import dan200.computercraft.api.lua.ILuaAPI import dan200.computercraft.api.lua.ILuaContext import dan200.computercraft.api.lua.MethodResult import dan200.computercraft.api.lua.ObjectArguments +import dan200.computercraft.core.apis.OSAPI import dan200.computercraft.core.apis.PeripheralAPI import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.time.Duration /** * The context for tasks which consume Lua objects. @@ -40,6 +42,19 @@ interface LuaTaskContext { /** Call a peripheral method. */ suspend fun LuaTaskContext.callPeripheral(name: String, method: String, vararg args: Any?): Array? = getApi().call(context, ObjectArguments(name, method, *args)).await() + + /** + * Sleep for the given duration. This uses the internal computer clock, so won't be accurate. + */ + suspend fun LuaTaskContext.sleep(duration: Duration) { + val timer = getApi().startTimer(duration.inWholeMilliseconds / 1000.0) + while (true) { + val event = pullEvent("timer") + if (event[0] == "timer" && event[1] is Number && (event[1] as Number).toInt() == timer) { + return + } + } + } } /** Get a registered API. */ diff --git a/src/testMod/java/dan200/computercraft/export/Exporter.java b/src/testMod/java/dan200/computercraft/export/Exporter.java index 7147a39f2..398c1fadc 100644 --- a/src/testMod/java/dan200/computercraft/export/Exporter.java +++ b/src/testMod/java/dan200/computercraft/export/Exporter.java @@ -10,8 +10,11 @@ import com.google.common.io.RecursiveDeleteOption; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.ingame.mod.TestMod; import net.minecraft.client.Minecraft; import net.minecraft.core.NonNullList; import net.minecraft.network.chat.Component; @@ -19,10 +22,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.*; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.ClientChatEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; import java.io.File; @@ -37,19 +36,24 @@ import java.util.Set; /** * Provides a {@literal /ccexport } command which exports icons and recipes for all ComputerCraft items. */ -@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT ) public class Exporter { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - @SubscribeEvent - public static void onClientCommands( ClientChatEvent event ) + public static void register( CommandDispatcher dispatcher ) { - String prefix = "/ccexport"; - if( !event.getMessage().startsWith( prefix ) ) return; - event.setCanceled( true ); + dispatcher.register( + LiteralArgumentBuilder.literal( "ccexport" ) + .then( RequiredArgumentBuilder.argument( "path", StringArgumentType.string() ) + .executes( c -> { + run( c.getArgument( "name", String.class ) ); + return 0; + } ) ) ); + } - Path output = new File( event.getMessage().substring( prefix.length() ).trim() ).getAbsoluteFile().toPath(); + private static void run( String path ) + { + Path output = new File( path ).getAbsoluteFile().toPath(); if( !Files.isDirectory( output ) ) { Minecraft.getInstance().gui.getChat().addMessage( Component.literal( "Output path does not exist" ) ); diff --git a/src/testMod/java/dan200/computercraft/ingame/api/ComputerState.java b/src/testMod/java/dan200/computercraft/gametest/api/ComputerState.java similarity index 93% rename from src/testMod/java/dan200/computercraft/ingame/api/ComputerState.java rename to src/testMod/java/dan200/computercraft/gametest/api/ComputerState.java index 34f64ad4d..d4b82a347 100644 --- a/src/testMod/java/dan200/computercraft/ingame/api/ComputerState.java +++ b/src/testMod/java/dan200/computercraft/gametest/api/ComputerState.java @@ -3,9 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.ingame.api; +package dan200.computercraft.gametest.api; -import dan200.computercraft.ingame.mod.TestAPI; +import dan200.computercraft.gametest.core.TestAPI; import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.gametest.framework.GameTestSequence; diff --git a/src/testMod/java/dan200/computercraft/gametest/api/GameTestHolder.java b/src/testMod/java/dan200/computercraft/gametest/api/GameTestHolder.java new file mode 100644 index 000000000..be4684cb6 --- /dev/null +++ b/src/testMod/java/dan200/computercraft/gametest/api/GameTestHolder.java @@ -0,0 +1,24 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest.api; + +import net.minecraft.gametest.framework.GameTest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks classes containing {@linkplain GameTest game tests}. + *

+ * This is used by Forge to automatically load and test classes. + */ +@Target( ElementType.TYPE ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface GameTestHolder +{ +} diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java b/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java similarity index 76% rename from src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java rename to src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java index 1bd85bf0e..de82eafe1 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/CCTestCommand.java +++ b/src/testMod/java/dan200/computercraft/gametest/core/CCTestCommand.java @@ -3,17 +3,16 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.ingame.mod; +package dan200.computercraft.gametest.core; import com.mojang.brigadier.CommandDispatcher; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.mixin.gametest.TestCommandAccessor; import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTestRegistry; import net.minecraft.gametest.framework.StructureUtils; -import net.minecraft.gametest.framework.TestCommand; import net.minecraft.gametest.framework.TestFunction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; @@ -24,10 +23,10 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.level.block.entity.StructureBlockEntity; import net.minecraft.world.level.storage.LevelResource; -import net.minecraftforge.fml.loading.FMLLoader; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice; import static net.minecraft.commands.Commands.literal; @@ -37,6 +36,8 @@ import static net.minecraft.commands.Commands.literal; */ class CCTestCommand { + public static final LevelResource LOCATION = new LevelResource( ComputerCraft.MOD_ID ); + public static void register( CommandDispatcher dispatcher ) { dispatcher.register( choice( "cctest" ) @@ -49,7 +50,7 @@ class CCTestCommand for( TestFunction function : GameTestRegistry.getAllTestFunctions() ) { - TestCommand.exportTestStructure( context.getSource(), function.getStructureName() ); + TestCommandAccessor.callExportTestStructure( context.getSource(), function.getStructureName() ); } return 0; } ) ) @@ -57,17 +58,11 @@ class CCTestCommand for( TestFunction function : GameTestRegistry.getAllTestFunctions() ) { dispatcher.execute( "test import " + function.getTestName(), context.getSource() ); - TestCommand.exportTestStructure( context.getSource(), function.getStructureName() ); + TestCommandAccessor.callExportTestStructure( context.getSource(), function.getStructureName() ); } return 0; } ) ) - .then( literal( "promote" ).executes( context -> { - if( !FMLLoader.getDist().isClient() ) return error( context.getSource(), "Cannot run on server" ); - - promote(); - return 0; - } ) ) .then( literal( "marker" ).executes( context -> { ServerPlayer player = context.getSource().getPlayerOrException(); BlockPos pos = StructureUtils.findNearestStructureBlock( player.blockPosition(), 15, player.getLevel() ); @@ -99,7 +94,7 @@ class CCTestCommand { try { - Copier.replicate( TestMod.sourceDir.resolve( "computers" ), server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ) ); + Copier.replicate( getSourceComputerPath(), getWorldComputerPath( server ) ); } catch( IOException e ) { @@ -111,7 +106,7 @@ class CCTestCommand { try { - Copier.replicate( server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ), TestMod.sourceDir.resolve( "computers" ) ); + Copier.replicate( getWorldComputerPath( server ), getSourceComputerPath() ); } catch( IOException e ) { @@ -119,20 +114,14 @@ class CCTestCommand } } - private static void promote() + private static Path getWorldComputerPath( MinecraftServer server ) { - try - { - Copier.replicate( - Minecraft.getInstance().gameDirectory.toPath().resolve( "screenshots" ), - TestMod.sourceDir.resolve( "screenshots" ), - x -> !x.toFile().getName().endsWith( ".diff.png" ) - ); - } - catch( IOException e ) - { - throw new UncheckedIOException( e ); - } + return server.getWorldPath( LOCATION ).resolve( "computer" ).resolve( "0" ); + } + + private static Path getSourceComputerPath() + { + return TestHooks.sourceDir.resolve( "computer" ); } private static int error( CommandSourceStack source, String message ) diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/ClientHooks.java b/src/testMod/java/dan200/computercraft/gametest/core/ClientHooks.java similarity index 96% rename from src/testMod/java/dan200/computercraft/ingame/mod/ClientHooks.java rename to src/testMod/java/dan200/computercraft/gametest/core/ClientHooks.java index a946dc107..78638ad66 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/ClientHooks.java +++ b/src/testMod/java/dan200/computercraft/gametest/core/ClientHooks.java @@ -3,7 +3,7 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.ingame.mod; +package dan200.computercraft.gametest.core; import net.minecraft.client.CloudStatus; import net.minecraft.client.Minecraft; @@ -17,7 +17,7 @@ import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT ) +@Mod.EventBusSubscriber( modid = "cctest", value = Dist.CLIENT ) public final class ClientHooks { private static final Logger LOG = LogManager.getLogger( TestHooks.class ); diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/Copier.java b/src/testMod/java/dan200/computercraft/gametest/core/Copier.java similarity index 97% rename from src/testMod/java/dan200/computercraft/ingame/mod/Copier.java rename to src/testMod/java/dan200/computercraft/gametest/core/Copier.java index 439ba152e..df198af9d 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/Copier.java +++ b/src/testMod/java/dan200/computercraft/gametest/core/Copier.java @@ -3,7 +3,7 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.ingame.mod; +package dan200.computercraft.gametest.core; import com.google.common.io.MoreFiles; import com.google.common.io.RecursiveDeleteOption; diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/TestAPI.java b/src/testMod/java/dan200/computercraft/gametest/core/TestAPI.java similarity index 94% rename from src/testMod/java/dan200/computercraft/ingame/mod/TestAPI.java rename to src/testMod/java/dan200/computercraft/gametest/core/TestAPI.java index 67e8876e2..c939f6900 100644 --- a/src/testMod/java/dan200/computercraft/ingame/mod/TestAPI.java +++ b/src/testMod/java/dan200/computercraft/gametest/core/TestAPI.java @@ -3,22 +3,22 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.ingame.mod; +package dan200.computercraft.gametest.core; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.IComputerSystem; import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; -import dan200.computercraft.ingame.api.ComputerState; -import dan200.computercraft.ingame.api.TestExtensionsKt; +import dan200.computercraft.gametest.api.ComputerState; +import dan200.computercraft.gametest.api.TestExtensionsKt; import net.minecraft.gametest.framework.GameTestSequence; import java.util.Optional; /** * API exposed to computers to help write tests. - * + *

* Note, we extend this API within startup file of computers (see {@code cctest.lua}). * * @see TestExtensionsKt#thenComputerOk(GameTestSequence, String, String) To check tests on the computer have passed. diff --git a/src/testMod/java/dan200/computercraft/gametest/core/TestHooks.java b/src/testMod/java/dan200/computercraft/gametest/core/TestHooks.java new file mode 100644 index 000000000..b106a325e --- /dev/null +++ b/src/testMod/java/dan200/computercraft/gametest/core/TestHooks.java @@ -0,0 +1,60 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest.core; + +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.gametest.api.Times; +import dan200.computercraft.shared.computer.core.ServerContext; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTestRunner; +import net.minecraft.gametest.framework.GameTestTicker; +import net.minecraft.gametest.framework.StructureUtils; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.Level; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestHooks +{ + public static final Logger LOGGER = LoggerFactory.getLogger( TestHooks.class ); + + public static final Path sourceDir = Paths.get( System.getProperty( "cctest.sources" ) ).normalize().toAbsolutePath(); + + public static void init() + { + ServerContext.luaMachine = ManagedComputers.INSTANCE; + ComputerCraftAPI.registerAPIFactory( TestAPI::new ); + StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString(); + } + + public static void onServerStarted( MinecraftServer server ) + { + GameRules rules = server.getGameRules(); + rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server ); + + ServerLevel world = server.getLevel( Level.OVERWORLD ); + if( world != null ) world.setDayTime( Times.NOON ); + + LOGGER.info( "Cleaning up after last run" ); + GameTestRunner.clearAllTests( server.overworld(), new BlockPos( 0, -60, 0 ), GameTestTicker.SINGLETON, 200 ); + + // Delete server context and add one with a mutable machine factory. This allows us to set the factory for + // specific test batches without having to reset all computers. + for( var computer : ServerContext.get( server ).registry().getComputers() ) + { + var label = computer.getLabel() == null ? "#" + computer.getID() : computer.getLabel(); + LOGGER.warn( "Unexpected computer {}", label ); + } + + LOGGER.info( "Importing files" ); + CCTestCommand.importFiles( server ); + } +} diff --git a/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java b/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java new file mode 100644 index 000000000..0244d86ff --- /dev/null +++ b/src/testMod/java/dan200/computercraft/gametest/core/TestMod.java @@ -0,0 +1,144 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest.core; + +import dan200.computercraft.export.Exporter; +import dan200.computercraft.gametest.api.GameTestHolder; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestRegistry; +import net.minecraft.gametest.framework.StructureUtils; +import net.minecraft.gametest.framework.TestFunction; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.client.event.RegisterClientCommandsEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.RegisterGameTestsEvent; +import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.forgespi.language.ModFileScanData; +import net.minecraftforge.gametest.PrefixGameTestTemplate; +import org.objectweb.asm.Type; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Locale; +import java.util.function.Consumer; + +@Mod( "cctest" ) +public class TestMod +{ + public TestMod() + { + TestHooks.init(); + + var bus = MinecraftForge.EVENT_BUS; + bus.addListener( EventPriority.LOW, ( ServerStartedEvent e ) -> TestHooks.onServerStarted( e.getServer() ) ); + bus.addListener( ( RegisterCommandsEvent e ) -> CCTestCommand.register( e.getDispatcher() ) ); + bus.addListener( ( RegisterClientCommandsEvent e ) -> Exporter.register( e.getDispatcher() ) ); + + var modBus = FMLJavaModLoadingContext.get().getModEventBus(); + modBus.addListener( ( RegisterGameTestsEvent event ) -> { + var holder = Type.getType( GameTestHolder.class ); + ModList.get().getAllScanData().stream() + .map( ModFileScanData::getAnnotations ) + .flatMap( Collection::stream ) + .filter( a -> holder.equals( a.annotationType() ) ) + .forEach( x -> registerClass( x.clazz().getClassName(), event::register ) ); + } ); + } + + + private static Class loadClass( String name ) + { + try + { + return Class.forName( name, true, TestMod.class.getClassLoader() ); + } + catch( ReflectiveOperationException e ) + { + throw new RuntimeException( e ); + } + } + + private static void registerClass( String className, Consumer fallback ) + { + var klass = loadClass( className ); + for( var method : klass.getDeclaredMethods() ) + { + var testInfo = method.getAnnotation( GameTest.class ); + if( testInfo == null ) + { + fallback.accept( method ); + continue; + } + + GameTestRegistry.getAllTestFunctions().add( turnMethodIntoTestFunction( method, testInfo ) ); + GameTestRegistry.getAllTestClassNames().add( className ); + } + } + + /** + * Custom implementation of {@link GameTestRegistry#turnMethodIntoTestFunction(Method)} which makes + * {@link GameTest#template()} behave the same as Fabric, namely in that it points to a {@link ResourceLocation}, + * rather than a test-class-specific structure. + *

+ * This effectively acts as a global version of {@link PrefixGameTestTemplate}, just one which doesn't require Forge + * to be present. + * + * @param method The method to register. + * @param testInfo The test info. + * @return The constructed test function. + */ + private static TestFunction turnMethodIntoTestFunction( Method method, GameTest testInfo ) + { + var className = method.getDeclaringClass().getSimpleName().toLowerCase( Locale.ROOT ); + var testName = className + "." + method.getName().toLowerCase( Locale.ROOT ); + return new TestFunction( + testInfo.batch(), + testName, + testInfo.template().isEmpty() ? testName : testInfo.template(), + StructureUtils.getRotationForRotationSteps( testInfo.rotationSteps() ), testInfo.timeoutTicks(), testInfo.setupTicks(), + testInfo.required(), testInfo.requiredSuccesses(), testInfo.attempts(), + turnMethodIntoConsumer( method ) + ); + } + + private static Consumer turnMethodIntoConsumer( Method method ) + { + return value -> { + try + { + Object instance = null; + if( !Modifier.isStatic( method.getModifiers() ) ) + { + instance = method.getDeclaringClass().getConstructor().newInstance(); + } + + method.invoke( instance, value ); + } + catch( InvocationTargetException e ) + { + if( e.getCause() instanceof RuntimeException ) + { + throw (RuntimeException) e.getCause(); + } + else + { + throw new RuntimeException( e.getCause() ); + } + } + catch( ReflectiveOperationException e ) + { + throw new RuntimeException( e ); + } + }; + } +} diff --git a/src/testMod/java/dan200/computercraft/ingame/Computer_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Computer_Test.kt deleted file mode 100644 index 9ba41826f..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Computer_Test.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.ingame.api.modifyBlock -import dan200.computercraft.ingame.api.sequence -import net.minecraft.core.BlockPos -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.world.level.block.LeverBlock -import net.minecraft.world.level.block.RedstoneLampBlock -import net.minecraftforge.gametest.GameTestHolder - -@GameTestHolder(ComputerCraft.MOD_ID) -class Computer_Test { - /** - * Ensures redstone signals do not travel through computers. - * - * @see [#548](https://github.com/cc-tweaked/CC-Tweaked/issues/548) - */ - @GameTest - fun No_through_signal(context: GameTestHelper) = context.sequence { - val lamp = BlockPos(2, 2, 4) - val lever = BlockPos(2, 2, 0) - this - .thenExecute { - context.assertBlockState(lamp, { !it.getValue(RedstoneLampBlock.LIT) }, { "Lamp should not be lit" }) - context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) } - } - .thenIdle(3) - .thenExecute { - context.assertBlockState( - lamp, - { !it.getValue(RedstoneLampBlock.LIT) }, - { "Lamp should still not be lit" }, - ) - } - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/CraftOs_Test.kt b/src/testMod/java/dan200/computercraft/ingame/CraftOs_Test.kt deleted file mode 100644 index 6740501ab..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/CraftOs_Test.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.ingame.api.sequence -import dan200.computercraft.ingame.api.thenComputerOk -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraftforge.gametest.GameTestHolder - -@GameTestHolder(ComputerCraft.MOD_ID) -class CraftOs_Test { - /** - * Sends a rednet message to another a computer and back again. - */ - @GameTest(timeoutTicks = 200) - fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk("main") } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/Disk_Drive_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Disk_Drive_Test.kt deleted file mode 100644 index 46b284612..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Disk_Drive_Test.kt +++ /dev/null @@ -1,29 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.ingame.api.sequence -import dan200.computercraft.ingame.api.thenComputerOk -import net.minecraft.core.BlockPos -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.world.item.Items -import net.minecraftforge.gametest.GameTestHolder - -@GameTestHolder(ComputerCraft.MOD_ID) -class Disk_Drive_Test { - /** - * Ensure audio disks exist and we can play them. - * - * @see [#688](https://github.com/cc-tweaked/CC-Tweaked/issues/688) - */ - @GameTest(timeoutTicks = Modem_Test.TIMEOUT) - fun Audio_disk(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - @GameTest(timeoutTicks = Modem_Test.TIMEOUT) - fun Ejects_disk(helper: GameTestHelper) = helper.sequence { - val stackAt = BlockPos(2, 2, 2) - this - .thenComputerOk() - .thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) } - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/Modem_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Modem_Test.kt deleted file mode 100644 index 6c518ee21..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Modem_Test.kt +++ /dev/null @@ -1,45 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.ingame.api.sequence -import dan200.computercraft.ingame.api.thenComputerOk -import dan200.computercraft.shared.Registry -import dan200.computercraft.shared.peripheral.modem.wired.BlockCable -import net.minecraft.core.BlockPos -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraftforge.gametest.GameTestHolder - -@GameTestHolder(ComputerCraft.MOD_ID) -class Modem_Test { - @GameTest(timeoutTicks = TIMEOUT) - fun Have_peripherals(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - @GameTest(timeoutTicks = TIMEOUT) - fun Gains_peripherals(helper: GameTestHelper) = helper.sequence { - val position = BlockPos(2, 2, 2) - this - .thenComputerOk(marker = "initial") - .thenExecute { - helper.setBlock( - position, - BlockCable.correctConnections( - helper.level, - helper.absolutePos(position), - Registry.ModBlocks.CABLE.get().defaultBlockState().setValue(BlockCable.CABLE, true), - ), - ) - } - .thenComputerOk() - } - - /** - * Sends a modem message to another computer on the same network - */ - @GameTest(timeoutTicks = TIMEOUT) - fun Transmits_messages(context: GameTestHelper) = context.sequence { thenComputerOk("receive") } - - companion object { - const val TIMEOUT = 200 - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/Monitor_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Monitor_Test.kt deleted file mode 100644 index 52813cf3a..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Monitor_Test.kt +++ /dev/null @@ -1,87 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.ingame.api.* -import dan200.computercraft.shared.Capabilities -import dan200.computercraft.shared.Registry -import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer -import dan200.computercraft.shared.peripheral.monitor.TileMonitor -import net.minecraft.commands.arguments.blocks.BlockInput -import net.minecraft.core.BlockPos -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.nbt.CompoundTag -import net.minecraft.world.level.block.Blocks -import net.minecraftforge.gametest.GameTestHolder -import java.util.* - -@GameTestHolder(ComputerCraft.MOD_ID) -class Monitor_Test { - @GameTest - fun Ensures_valid_on_place(context: GameTestHelper) = context.sequence { - val pos = BlockPos(2, 2, 2) - val tag = CompoundTag() - tag.putInt("Width", 2) - tag.putInt("Height", 2) - - val toSet = BlockInput( - Registry.ModBlocks.MONITOR_ADVANCED.get().defaultBlockState(), - Collections.emptySet(), - tag, - ) - - context.setBlock(pos, Blocks.AIR.defaultBlockState()) - context.setBlock(pos, toSet) - - this - .thenIdle(2) - .thenExecute { - val tile = context.getBlockEntity(pos) - if (tile !is TileMonitor) { - context.fail("Expected tile to be monitor, is $tile", pos) - return@thenExecute - } - - if (tile.width != 1 || tile.height != 1) { - context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos) - } - } - } - - private fun looksAcceptable(helper: GameTestHelper, renderer: MonitorRenderer) = helper.sequence { - this - .thenExecute { - ComputerCraft.monitorRenderer = renderer - helper.positionAtArmorStand() - - // Get the monitor and peripheral. This forces us to create a server monitor at this location. - val monitor = helper.getBlockEntity(BlockPos(2, 2, 3), Registry.ModBlockEntities.MONITOR_ADVANCED.get()) - monitor.getCapability(Capabilities.CAPABILITY_PERIPHERAL) - - val terminal = monitor.cachedServerMonitor!!.terminal!! - terminal.write("Hello, world!") - terminal.setCursorPos(1, 2) - terminal.textColour = 2 - terminal.backgroundColour = 3 - terminal.write("Some coloured text") - } - .thenScreenshot() - } - - // @GameTest(batch = "Monitor_Test.Looks_acceptable", template = LOOKS_ACCEPTABLE) - fun Looks_acceptable(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO) - - // @GameTest(batch = "Monitor_Test.Looks_acceptable_dark", template = LOOKS_ACCEPTABLE_DARK) - fun Looks_acceptable_dark(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO) - - // @GameTest(batch = "Monitor_Test.Looks_acceptable_vbo", template = LOOKS_ACCEPTABLE) - fun Looks_acceptable_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO) - - // @GameTest(batch = "Monitor_Test.Looks_acceptable_dark_vbo", template = LOOKS_ACCEPTABLE_DARK) - fun Looks_acceptable_dark_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO) - - private companion object { - const val LOOKS_ACCEPTABLE = "looks_acceptable" - const val LOOKS_ACCEPTABLE_DARK = "looks_acceptable_dark" - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/Printout_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Printout_Test.kt deleted file mode 100644 index 4975b1902..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Printout_Test.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ingame.api.positionAtArmorStand -import dan200.computercraft.ingame.api.sequence -import dan200.computercraft.ingame.api.thenScreenshot -import net.minecraft.gametest.framework.GameTestHelper - -class Printout_Test { - // @GameTest(batch = "Printout_Test.In_frame_at_night", timeoutTicks = Timeouts.CLIENT_TIMEOUT) - fun In_frame_at_night(helper: GameTestHelper) = helper.sequence { - this - .thenExecute { helper.positionAtArmorStand() } - .thenScreenshot() - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/Turtle_Test.kt b/src/testMod/java/dan200/computercraft/ingame/Turtle_Test.kt deleted file mode 100644 index 9147a7950..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/Turtle_Test.kt +++ /dev/null @@ -1,101 +0,0 @@ -package dan200.computercraft.ingame - -import dan200.computercraft.ComputerCraft -import dan200.computercraft.api.detail.BasicItemDetailProvider -import dan200.computercraft.api.detail.DetailRegistries -import dan200.computercraft.ingame.api.* -import dan200.computercraft.ingame.api.Timeouts.COMPUTER_TIMEOUT -import dan200.computercraft.shared.media.items.ItemPrintout -import net.minecraft.gametest.framework.GameTest -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.world.item.ItemStack -import net.minecraftforge.gametest.GameTestHolder - -@GameTestHolder(ComputerCraft.MOD_ID) -class Turtle_Test { - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can sheer sheep (and drop items) - * - * @see [#537](https://github.com/cc-tweaked/CC-Tweaked/issues/537) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can place lava. - * - * @see [#518](https://github.com/cc-tweaked/CC-Tweaked/issues/518) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can place when waterlogged. - * - * @see [#385](https://github.com/cc-tweaked/CC-Tweaked/issues/385) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can pick up lava - * - * @see [#297](https://github.com/cc-tweaked/CC-Tweaked/issues/297) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can hoe dirt. - * - * @see [#258](https://github.com/cc-tweaked/CC-Tweaked/issues/258) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can place monitors - * - * @see [#691](https://github.com/cc-tweaked/CC-Tweaked/issues/691) - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can place into compostors. These are non-typical inventories, so - * worth testing. - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can be cleaned in cauldrons. - * - * Currently not required as turtles can no longer right-click cauldrons. - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT, required = false) - fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - - /** - * Checks turtles can use IDetailProviders by getting details for a printed page. - */ - @GameTest(timeoutTicks = COMPUTER_TIMEOUT) - fun Item_detail_provider(helper: GameTestHelper) = helper.sequence { - this - .thenComputerOk(marker = "initial") - .thenExecute { - // Register a dummy provider for printout items - DetailRegistries.ITEM_STACK.addProvider( - object : BasicItemDetailProvider("printout", ItemPrintout::class.java) { - override fun provideDetails(data: MutableMap, stack: ItemStack, item: ItemPrintout) { - data["type"] = item.type.toString() - } - }, - ) - } - .thenComputerOk() - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/api/TestExtensions.kt b/src/testMod/java/dan200/computercraft/ingame/api/TestExtensions.kt deleted file mode 100644 index 608f4aa22..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/api/TestExtensions.kt +++ /dev/null @@ -1,183 +0,0 @@ -package dan200.computercraft.ingame.api - -import dan200.computercraft.ingame.mod.ImageUtils -import dan200.computercraft.ingame.mod.TestMod -import net.minecraft.client.Minecraft -import net.minecraft.client.Screenshot -import net.minecraft.commands.arguments.blocks.BlockInput -import net.minecraft.core.BlockPos -import net.minecraft.gametest.framework.GameTestAssertException -import net.minecraft.gametest.framework.GameTestAssertPosException -import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.gametest.framework.GameTestSequence -import net.minecraft.resources.ResourceLocation -import net.minecraft.world.entity.decoration.ArmorStand -import net.minecraft.world.level.block.entity.BlockEntity -import net.minecraft.world.level.block.entity.BlockEntityType -import net.minecraft.world.level.block.state.BlockState -import net.minecraftforge.registries.ForgeRegistries -import java.nio.file.Files -import java.util.concurrent.CompletableFuture -import java.util.concurrent.ExecutionException -import java.util.concurrent.atomic.AtomicBoolean -import java.util.function.Supplier -import javax.imageio.ImageIO - -object Times { - const val NOON: Long = 6000 -} - -/** - * Custom timeouts for various test types. - */ -object Timeouts { - private const val SECOND: Int = 20 - - const val COMPUTER_TIMEOUT: Int = SECOND * 15 - - const val CLIENT_TIMEOUT: Int = SECOND * 20 -} - -/** - * Wait until a computer has finished running and check it is OK. - */ -fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = ComputerState.DONE): GameTestSequence { - val label = parent.testName + (if (name == null) "" else ".$name") - return this.thenWaitUntil { - val computer = ComputerState.get(label) - if (computer == null || !computer.isDone(marker)) throw GameTestAssertException("Computer '$label' has not reached $marker yet.") - }.thenExecute { - ComputerState.get(label).check(marker) - } -} - -/** - * Run a task on the client - */ -fun GameTestSequence.thenOnClient(task: ClientTestHelper.() -> Unit): GameTestSequence { - var future: CompletableFuture? = null - return this - .thenExecute { future = Minecraft.getInstance().submit(Supplier { task(ClientTestHelper()) }) } - .thenWaitUntil { if (!future!!.isDone) throw GameTestAssertException("Not done task yet") } - .thenExecute { - try { - future!!.get() - } catch (e: ExecutionException) { - throw e.cause ?: e - } - } -} - -/** - * Idle for one tick to allow the client to catch up, then take a screenshot. - */ -fun GameTestSequence.thenScreenshot(name: String? = null): GameTestSequence { - val suffix = if (name == null) "" else "-$name" - val fullName = "${parent.testName}$suffix" - - var counter = 0 - val hasScreenshot = AtomicBoolean() - - return this - // Wait until all chunks have been rendered and we're idle for an extended period. - .thenExecute { counter = 0 } - .thenWaitUntil { - val renderer = Minecraft.getInstance().levelRenderer - if (renderer.chunkRenderDispatcher != null && renderer.hasRenderedAllChunks()) { - val idleFor = ++counter - if (idleFor <= 20) throw GameTestAssertException("Only idle for $idleFor ticks") - } else { - counter = 0 - throw GameTestAssertException("Waiting for client to finish rendering") - } - } - // Now disable the GUI, take a screenshot and reenable it. We sleep either side to give the client time to do - // its thing. - .thenExecute { - Minecraft.getInstance().options.hideGui = true - hasScreenshot.set(false) - } - .thenIdle(5) // Some delay before/after to ensure the render thread has caught up. - .thenOnClient { screenshot("$fullName.png") { hasScreenshot.set(true) } } - .thenWaitUntil { if (!hasScreenshot.get()) throw GameTestAssertException("Screenshot does not exist") } - .thenExecute { - Minecraft.getInstance().options.hideGui = false - - val screenshotsPath = Minecraft.getInstance().gameDirectory.toPath().resolve("screenshots") - val screenshotPath = screenshotsPath.resolve("$fullName.png") - val originalPath = TestMod.sourceDir.resolve("screenshots").resolve("$fullName.png") - - if (!Files.exists(originalPath)) throw GameTestAssertException("$fullName does not exist. Use `/cctest promote' to create it.") - - val screenshot = ImageIO.read(screenshotPath.toFile()) - ?: throw GameTestAssertException("Error reading screenshot from $screenshotPath") - val original = ImageIO.read(originalPath.toFile()) - - if (screenshot.width != original.width || screenshot.height != original.height) { - throw GameTestAssertException("$fullName screenshot is ${screenshot.width}x${screenshot.height} but original is ${original.width}x${original.height}") - } - - ImageUtils.writeDifference(screenshotsPath.resolve("$fullName.diff.png"), screenshot, original) - if (!ImageUtils.areSame(screenshot, original)) throw GameTestAssertException("Images are different.") - } -} - -val GameTestHelper.testName: String get() = testInfo.testName - -val GameTestHelper.structureName: String get() = testInfo.structureName - -/** - * Modify a block state within the test. - */ -fun GameTestHelper.modifyBlock(pos: BlockPos, modify: (BlockState) -> BlockState) { - setBlock(pos, modify(getBlockState(pos))) -} - -fun GameTestHelper.sequence(run: GameTestSequence.() -> GameTestSequence) { - run(startSequence()).thenSucceed() -} - -private fun getName(type: BlockEntityType<*>): ResourceLocation = ForgeRegistries.BLOCK_ENTITY_TYPES.getKey(type)!! - -fun GameTestHelper.getBlockEntity(pos: BlockPos, type: BlockEntityType): T { - val tile = getBlockEntity(pos) - @Suppress("UNCHECKED_CAST") - return when { - tile == null -> throw GameTestAssertPosException("Expected ${getName(type)}, but no tile was there", absolutePos(pos), pos, 0) - tile.type != type -> throw GameTestAssertPosException("Expected ${getName(type)} but got ${getName(tile.type)}", absolutePos(pos), pos, 0) - else -> tile as T - } -} - -/** - * Set a block within the test structure. - */ -fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3) - -/** - * Position the player at an armor stand. - */ -fun GameTestHelper.positionAtArmorStand() { - val entities = level.getEntities(null, bounds) { it.name.string == structureName } - if (entities.size <= 0 || entities[0] !is ArmorStand) throw GameTestAssertException("Cannot find armor stand") - - val stand = entities[0] as ArmorStand - val player = level.randomPlayer ?: throw GameTestAssertException("Player does not exist") - - player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot) -} - -class ClientTestHelper { - val minecraft: Minecraft = Minecraft.getInstance() - - fun screenshot(name: String, callback: () -> Unit = {}) { - Screenshot.grab( - minecraft.gameDirectory, - name, - minecraft.mainRenderTarget, - ) { - TestMod.log.info(it.string) - callback() - } - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/ImageUtils.java b/src/testMod/java/dan200/computercraft/ingame/mod/ImageUtils.java deleted file mode 100644 index 6739fe7d7..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/mod/ImageUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.ingame.mod; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.nio.file.Path; - -public final class ImageUtils -{ - private static final Logger LOG = LogManager.getLogger( ImageUtils.class ); - - /** - * Allow 0.3% of pixels to fail. This allows for slight differences at the edges. - */ - private static final double PIXEL_THRESHOLD = 0.003; - - /** - * Maximum possible distance between two colours. Floating point differences means we need some fuzziness here. - */ - public static final int DISTANCE_THRESHOLD = 5; - - private ImageUtils() - { - } - - public static boolean areSame( BufferedImage left, BufferedImage right ) - { - int width = left.getWidth(), height = left.getHeight(); - if( width != right.getWidth() || height != right.getHeight() ) return false; - - int failed = 0, threshold = (int) (width * height * PIXEL_THRESHOLD); - for( int x = 0; x < width; x++ ) - { - for( int y = 0; y < height; y++ ) - { - int l = left.getRGB( x, y ), r = right.getRGB( x, y ); - if( (l & 0xFFFFFF) != (r & 0xFFFFFF) && distance( l, r, 0 ) + distance( l, r, 8 ) + distance( l, r, 16 ) >= DISTANCE_THRESHOLD ) - { - failed++; - } - } - } - - if( failed > 0 ) LOG.warn( "{} pixels failed comparing (threshold is {})", failed, threshold ); - return failed <= threshold; - } - - public static void writeDifference( Path path, BufferedImage left, BufferedImage right ) throws IOException - { - int width = left.getWidth(), height = left.getHeight(); - - BufferedImage copy = new BufferedImage( width, height, left.getType() ); - for( int x = 0; x < width; x++ ) - { - for( int y = 0; y < height; y++ ) - { - int l = left.getRGB( x, y ), r = right.getRGB( x, y ); - copy.setRGB( x, y, difference( l, r, 0 ) | difference( l, r, 8 ) | difference( l, r, 16 ) | 0xFF000000 ); - } - } - - ImageIO.write( copy, "png", path.toFile() ); - } - - private static int difference( int l, int r, int shift ) - { - return Math.abs( ((l >> shift) & 0xFF) - ((r >> shift) & 0xFF) ) << shift; - } - - private static int distance( int l, int r, int shift ) - { - return Math.abs( ((l >> shift) & 0xFF) - ((r >> shift) & 0xFF) ); - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java b/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java deleted file mode 100644 index d5338ac97..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/mod/TestHooks.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.ingame.mod; - -import dan200.computercraft.ingame.api.Times; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.GameRules; -import net.minecraft.world.level.Level; -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.server.ServerStartedEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -@Mod.EventBusSubscriber( modid = TestMod.MOD_ID ) -public class TestHooks -{ - private static final Logger LOG = LogManager.getLogger( TestHooks.class ); - - @SubscribeEvent - public static void onRegisterCommands( RegisterCommandsEvent event ) - { - LOG.info( "Starting server, registering command helpers." ); - CCTestCommand.register( event.getDispatcher() ); - } - - @SubscribeEvent - public static void onServerStarted( ServerStartedEvent event ) - { - MinecraftServer server = event.getServer(); - GameRules rules = server.getGameRules(); - rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server ); - - ServerLevel world = event.getServer().getLevel( Level.OVERWORLD ); - if( world != null ) world.setDayTime( Times.NOON ); - - // LOG.info( "Cleaning up after last run" ); - // CommandSourceStack source = server.createCommandSourceStack(); - // GameTestRunner.clearAllTests( source.getLevel(), getStart( source ), GameTestTicker.SINGLETON, 200 ); - - LOG.info( "Importing files" ); - CCTestCommand.importFiles( server ); - } -} diff --git a/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java b/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java deleted file mode 100644 index fffd15ef0..000000000 --- a/src/testMod/java/dan200/computercraft/ingame/mod/TestMod.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.ingame.mod; - -import dan200.computercraft.api.ComputerCraftAPI; -import net.minecraft.gametest.framework.StructureUtils; -import net.minecraftforge.fml.common.Mod; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.nio.file.Path; -import java.nio.file.Paths; - -@Mod( TestMod.MOD_ID ) -public class TestMod -{ - public static final Path sourceDir = Paths.get( "../../src/testMod/server-files" ).normalize().toAbsolutePath(); - - public static final String MOD_ID = "cctest"; - - public static final Logger log = LogManager.getLogger( MOD_ID ); - - public TestMod() - { - log.info( "CC: Test initialised" ); - ComputerCraftAPI.registerAPIFactory( TestAPI::new ); - - StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString(); - } -} diff --git a/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestHelperAccessor.java b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestHelperAccessor.java new file mode 100644 index 000000000..8e66a929f --- /dev/null +++ b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestHelperAccessor.java @@ -0,0 +1,23 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin.gametest; + +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.world.phys.AABB; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin( GameTestHelper.class ) +public interface GameTestHelperAccessor +{ + @Invoker + AABB callGetBounds(); + + @Accessor + GameTestInfo getTestInfo(); +} diff --git a/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestInfoAccessor.java b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestInfoAccessor.java new file mode 100644 index 000000000..b7e2a1846 --- /dev/null +++ b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestInfoAccessor.java @@ -0,0 +1,17 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin.gametest; + +import net.minecraft.gametest.framework.GameTestInfo; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin( GameTestInfo.class ) +public interface GameTestInfoAccessor +{ + @Invoker( "getTick" ) + long computercraft$getTick(); +} diff --git a/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceAccessor.java b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceAccessor.java new file mode 100644 index 000000000..9e9dad1ab --- /dev/null +++ b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceAccessor.java @@ -0,0 +1,18 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin.gametest; + +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.gametest.framework.GameTestSequence; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin( GameTestSequence.class ) +public interface GameTestSequenceAccessor +{ + @Accessor + GameTestInfo getParent(); +} diff --git a/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceMixin.java b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceMixin.java new file mode 100644 index 000000000..efc7293c7 --- /dev/null +++ b/src/testMod/java/dan200/computercraft/mixin/gametest/GameTestSequenceMixin.java @@ -0,0 +1,58 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin.gametest; + +import dan200.computercraft.gametest.core.TestHooks; +import net.minecraft.gametest.framework.GameTestAssertException; +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.gametest.framework.GameTestSequence; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin( GameTestSequence.class ) +class GameTestSequenceMixin +{ + @Shadow + @Final + GameTestInfo parent; + + /** + * Override {@link GameTestSequence#tickAndContinue(long)} to catch non-{@link GameTestAssertException} failures. + * + * @param ticks The current tick. + * @author Jonathan Coates + * @reason There's no sense doing this in a more compatible way for game tests. + */ + @Overwrite + public void tickAndContinue( long ticks ) + { + try + { + tick( ticks ); + } + catch( GameTestAssertException ignored ) + { + // Mimic the original behaviour. + } + catch( AssertionError e ) + { + parent.fail( e ); + } + catch( Exception e ) + { + // Fail the test, rather than crashing the server. + TestHooks.LOGGER.error( "{} threw unexpected exception", parent.getTestName(), e ); + parent.fail( e ); + } + } + + @Shadow + private void tick( long tick ) + { + } +} diff --git a/src/testMod/java/dan200/computercraft/mixin/gametest/TestCommandAccessor.java b/src/testMod/java/dan200/computercraft/mixin/gametest/TestCommandAccessor.java new file mode 100644 index 000000000..70ce8ee55 --- /dev/null +++ b/src/testMod/java/dan200/computercraft/mixin/gametest/TestCommandAccessor.java @@ -0,0 +1,21 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.mixin.gametest; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.gametest.framework.TestCommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin( TestCommand.class ) +public interface TestCommandAccessor +{ + @Invoker + static int callExportTestStructure( CommandSourceStack source, String structure ) + { + return 0; + } +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt new file mode 100644 index 000000000..581c5a712 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt @@ -0,0 +1,67 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.core.apis.RedstoneAPI +import dan200.computercraft.core.computer.ComputerSide +import dan200.computercraft.gametest.api.* +import dan200.computercraft.test.core.computer.getApi +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.LeverBlock +import net.minecraft.world.level.block.RedstoneLampBlock + +@GameTestHolder +class Computer_Test { + /** + * Ensures redstone signals do not travel through computers. + * + * @see [#548](https://github.com/cc-tweaked/CC-Tweaked/issues/548) + */ + @GameTest + fun No_through_signal(context: GameTestHelper) = context.sequence { + val lamp = BlockPos(2, 2, 4) + val lever = BlockPos(2, 2, 0) + thenExecute { + context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") + context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) } + } + thenIdle(3) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") } + } + + /** + * Similar to the above, but with a repeater before the computer + */ + @GameTest + fun No_through_signal_reverse(context: GameTestHelper) = context.sequence { + val lamp = BlockPos(2, 2, 4) + val lever = BlockPos(2, 2, 0) + thenExecute { + context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") + context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) } + } + thenIdle(3) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") } + } + + @GameTest + fun Set_and_destroy(context: GameTestHelper) = context.sequence { + val lamp = BlockPos(2, 2, 3) + + thenOnComputer { getApi().setOutput(ComputerSide.BACK, true) } + thenIdle(3) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, true, "Lamp should be lit") } + thenExecute { context.setBlock(BlockPos(2, 2, 2), Blocks.AIR) } + thenIdle(4) + thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") } + } + + // TODO: More redstone connectivity tests! + // TODO: Computer peripherals (including command computers) +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/CraftOs_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/CraftOs_Test.kt new file mode 100644 index 000000000..0ec7e3427 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/CraftOs_Test.kt @@ -0,0 +1,22 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.gametest.api.GameTestHolder +import dan200.computercraft.gametest.api.Timeouts +import dan200.computercraft.gametest.api.sequence +import dan200.computercraft.gametest.api.thenComputerOk +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper + +@GameTestHolder +class CraftOs_Test { + /** + * Sends a rednet message to another a computer and back again. + */ + @GameTest(timeoutTicks = Timeouts.COMPUTER_TIMEOUT) + fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk("main") } +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Disk_Drive_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Disk_Drive_Test.kt new file mode 100644 index 000000000..3076f925f --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Disk_Drive_Test.kt @@ -0,0 +1,43 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.gametest.api.GameTestHolder +import dan200.computercraft.gametest.api.sequence +import dan200.computercraft.gametest.api.thenOnComputer +import dan200.computercraft.test.core.assertArrayEquals +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.world.item.Items + +@GameTestHolder +class Disk_Drive_Test { + /** + * Ensure audio disks exist and we can play them. + * + * @see [#688](https://github.com/cc-tweaked/CC-Tweaked/issues/688) + */ + @GameTest + fun Audio_disk(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + callPeripheral("right", "hasAudio") + .assertArrayEquals(true, message = "Disk has audio") + + callPeripheral("right", "getAudioTitle") + .assertArrayEquals("C418 - 13", message = "Correct audio title") + } + } + + @GameTest + fun Ejects_disk(helper: GameTestHelper) = helper.sequence { + val stackAt = BlockPos(2, 2, 2) + thenOnComputer { callPeripheral("right", "ejectDisk") } + thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) } + } + + // TODO: Ejecting disk unmounts and closes files +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Loot_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Loot_Test.kt new file mode 100644 index 000000000..dd5b52121 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Loot_Test.kt @@ -0,0 +1,37 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.gametest.api.GameTestHolder +import dan200.computercraft.gametest.api.Structures +import dan200.computercraft.gametest.api.sequence +import dan200.computercraft.shared.Registry +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.entity.ChestBlockEntity +import net.minecraft.world.level.storage.loot.BuiltInLootTables + +@GameTestHolder +class Loot_Test { + /** + * Test that the loot tables will spawn in treasure disks. + */ + @GameTest(template = Structures.DEFAULT) + fun Chest_contains_disk(context: GameTestHelper) = context.sequence { + thenExecute { + val pos = BlockPos(2, 2, 2) + + context.setBlock(pos, Blocks.CHEST) + val chest = context.getBlockEntity(pos) as ChestBlockEntity + chest.setLootTable(BuiltInLootTables.SIMPLE_DUNGEON, 123) + chest.unpackLootTable(null) + + context.assertContainerContains(pos, Registry.ModItems.TREASURE_DISK.get()) + } + } +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt new file mode 100644 index 000000000..a4b454ba3 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt @@ -0,0 +1,102 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.api.lua.ObjectArguments +import dan200.computercraft.core.apis.PeripheralAPI +import dan200.computercraft.core.computer.ComputerSide +import dan200.computercraft.gametest.api.* +import dan200.computercraft.shared.peripheral.modem.wired.BlockCable +import dan200.computercraft.test.core.assertArrayEquals +import dan200.computercraft.test.core.computer.LuaTaskContext +import dan200.computercraft.test.core.computer.getApi +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import org.junit.jupiter.api.Assertions.assertEquals +import kotlin.time.Duration.Companion.milliseconds + +@GameTestHolder +class Modem_Test { + @GameTest + fun Have_peripherals(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + assertEquals(listOf("monitor_0", "printer_0", "right"), getPeripheralNames(), "Starts with peripherals") + } + } + + @GameTest + fun Gains_peripherals(helper: GameTestHelper) = helper.sequence { + val position = BlockPos(2, 2, 2) + thenOnComputer { + assertEquals(listOf("back"), getPeripheralNames(), "Starts with peripherals") + } + thenExecute { + helper.setBlock( + position, + BlockCable.correctConnections( + helper.level, + helper.absolutePos(position), + dan200.computercraft.shared.Registry.ModBlocks.CABLE.get().defaultBlockState().setValue(BlockCable.CABLE, true), + ), + ) + } + thenIdle(1) + thenOnComputer { + assertEquals(listOf("back", "monitor_1", "printer_1"), getPeripheralNames(), "Gains new peripherals") + } + } + + /** + * Sends a modem message to another computer on the same network + */ + @GameTest + fun Transmits_messages(context: GameTestHelper) = context.sequence { + thenStartComputer("send") { + val modem = findPeripheral("modem") ?: throw IllegalStateException("Cannot find modem") + while (true) { + callPeripheral(modem, "transmit", 12, 34, "Hello") + sleep(50.milliseconds) + } + } + thenOnComputer("receive") { + val modem = findPeripheral("modem") ?: throw IllegalStateException("Cannot find modem") + callPeripheral(modem, "open", 12) + + pullEvent("modem_message") + .assertArrayEquals("modem_message", "left", 12, 34, "Hello", 4, message = "Modem message") + } + } +} + +private fun LuaTaskContext.findPeripheral(type: String): String? { + val peripheral = getApi() + for (side in ComputerSide.NAMES) { + val hasType = peripheral.hasType(side, type) + if (hasType != null && hasType[0] == true) return side + } + + return null +} + +private suspend fun LuaTaskContext.getPeripheralNames(): List { + val peripheral = getApi() + val peripherals = mutableListOf() + for (side in ComputerSide.NAMES) { + if (!peripheral.isPresent(side)) continue + peripherals.add(side) + + val hasType = peripheral.hasType(side, "modem") + if (hasType == null || hasType[0] != true) continue + + val names = peripheral.call(context, ObjectArguments(side, "getNamesRemote")).await() ?: continue + @Suppress("UNCHECKED_CAST") + peripherals.addAll(names[0] as Collection) + } + + peripherals.sort() + return peripherals +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Monitor_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Monitor_Test.kt new file mode 100644 index 000000000..e34ba16d3 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Monitor_Test.kt @@ -0,0 +1,50 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.gametest.api.GameTestHolder +import dan200.computercraft.gametest.api.getBlockEntity +import dan200.computercraft.gametest.api.sequence +import dan200.computercraft.gametest.api.setBlock +import dan200.computercraft.shared.Registry +import net.minecraft.commands.arguments.blocks.BlockInput +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.nbt.CompoundTag +import net.minecraft.world.level.block.Blocks +import java.util.* + +@GameTestHolder +class Monitor_Test { + @GameTest + fun Ensures_valid_on_place(context: GameTestHelper) = context.sequence { + val pos = BlockPos(2, 2, 2) + + thenExecute { + val tag = CompoundTag() + tag.putInt("Width", 2) + tag.putInt("Height", 2) + + val toSet = BlockInput( + Registry.ModBlocks.MONITOR_ADVANCED.get().defaultBlockState(), + Collections.emptySet(), + tag, + ) + + context.setBlock(pos, Blocks.AIR.defaultBlockState()) + context.setBlock(pos, toSet) + } + thenIdle(2) + thenExecute { + val tile = context.getBlockEntity(pos, Registry.ModBlockEntities.MONITOR_ADVANCED.get()) + + if (tile.width != 1 || tile.height != 1) { + context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos) + } + } + } +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt new file mode 100644 index 000000000..4e9bfdc5a --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Recipe_Test.kt @@ -0,0 +1,61 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.gametest.api.GameTestHolder +import dan200.computercraft.gametest.api.Structures +import dan200.computercraft.gametest.api.sequence +import dan200.computercraft.shared.Registry +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestAssertException +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.nbt.CompoundTag +import net.minecraft.world.entity.player.Player +import net.minecraft.world.inventory.AbstractContainerMenu +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.inventory.MenuType +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.world.item.crafting.CraftingRecipe +import net.minecraft.world.item.crafting.RecipeType +import org.junit.jupiter.api.Assertions.assertEquals +import java.util.* + +@GameTestHolder +class Recipe_Test { + /** + * Test that crafting results contain NBT data. + * + * Mostly useful for Fabric, where we need a mixin for this. + */ + @GameTest(template = Structures.DEFAULT) + fun Craft_result_has_nbt(context: GameTestHelper) = context.sequence { + thenExecute { + val container = CraftingContainer(DummyMenu, 3, 3) + container.setItem(0, ItemStack(Items.SKELETON_SKULL)) + container.setItem(1, ItemStack(Registry.ModItems.COMPUTER_ADVANCED.get())) + + val recipe: Optional = context.level.server.recipeManager + .getRecipeFor(RecipeType.CRAFTING, container, context.level) + if (!recipe.isPresent) throw GameTestAssertException("No recipe matches") + + val result = recipe.get().assemble(container) + + val owner = CompoundTag() + owner.putString("Name", "dan200") + owner.putString("Id", "f3c8d69b-0776-4512-8434-d1b2165909eb") + val tag = CompoundTag() + tag.put("SkullOwner", owner) + + assertEquals(tag, result.tag, "Expected NBT tags to be the same") + } + } + + object DummyMenu : AbstractContainerMenu(MenuType.GENERIC_9x1, 0) { + override fun quickMoveStack(player: Player, slot: Int): ItemStack = ItemStack.EMPTY + override fun stillValid(p0: Player): Boolean = true + } +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt new file mode 100644 index 000000000..41d378c6d --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt @@ -0,0 +1,252 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest + +import dan200.computercraft.api.detail.BasicItemDetailProvider +import dan200.computercraft.api.detail.DetailRegistries +import dan200.computercraft.api.lua.ObjectArguments +import dan200.computercraft.core.apis.PeripheralAPI +import dan200.computercraft.gametest.api.* +import dan200.computercraft.shared.Registry +import dan200.computercraft.shared.media.items.ItemPrintout +import dan200.computercraft.shared.peripheral.monitor.BlockMonitor +import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState +import dan200.computercraft.shared.turtle.apis.TurtleAPI +import dan200.computercraft.test.core.assertArrayEquals +import dan200.computercraft.test.core.computer.LuaTaskContext +import dan200.computercraft.test.core.computer.getApi +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.GameTest +import net.minecraft.gametest.framework.GameTestHelper +import net.minecraft.world.entity.EntityType +import net.minecraft.world.entity.item.PrimedTnt +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.FenceBlock +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.array +import org.hamcrest.Matchers.instanceOf +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import java.util.* + +@GameTestHolder +class Turtle_Test { + @GameTest + fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().getType("right").assertArrayEquals("modem", message = "Starts with a modem") + getApi().equipRight().await() + getApi().getType("right").assertArrayEquals("drive", message = "Unequipping gives a drive") + } + } + + /** + * Checks turtles can sheer sheep (and drop items) + * + * @see [#537](https://github.com/cc-tweaked/CC-Tweaked/issues/537) + */ + @GameTest + fun Shears_sheep(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().placeDown(ObjectArguments()).await() + .assertArrayEquals(true, message = "Shears the sheep") + + assertEquals("minecraft:white_wool", getTurtleItemDetail(2)["name"]) + } + } + + /** + * Checks turtles can place lava. + * + * @see [#518](https://github.com/cc-tweaked/CC-Tweaked/issues/518) + */ + @GameTest + fun Place_lava(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().placeDown(ObjectArguments()).await() + .assertArrayEquals(true, message = "Placed lava") + } + thenExecute { helper.assertBlockPresent(Blocks.LAVA, BlockPos(2, 2, 2)) } + } + + /** + * Checks turtles can place when waterlogged. + * + * @see [#385](https://github.com/cc-tweaked/CC-Tweaked/issues/385) + */ + @GameTest + fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().place(ObjectArguments()).await() + .assertArrayEquals(true, message = "Placed oak fence") + } + thenExecute { + helper.assertBlockIs(BlockPos(2, 2, 2), { it.block == Blocks.OAK_FENCE && it.getValue(FenceBlock.WATERLOGGED) }) + } + } + + /** + * Checks turtles can pick up lava + * + * @see [#297](https://github.com/cc-tweaked/CC-Tweaked/issues/297) + */ + @GameTest + fun Gather_lava(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().placeDown(ObjectArguments()).await() + .assertArrayEquals(true, message = "Picked up lava") + + assertEquals("minecraft:lava_bucket", getTurtleItemDetail()["name"]) + } + thenExecute { helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2)) } + } + + /** + * Checks turtles can hoe dirt. + * + * @see [#258](https://github.com/cc-tweaked/CC-Tweaked/issues/258) + */ + @GameTest + fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().dig(Optional.empty()).await() + .assertArrayEquals(true, message = "Dug with hoe") + } + thenExecute { helper.assertBlockPresent(Blocks.FARMLAND, BlockPos(1, 2, 1)) } + } + + /** + * Checks turtles can place monitors + * + * @see [#691](https://github.com/cc-tweaked/CC-Tweaked/issues/691) + */ + @GameTest + fun Place_monitor(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().place(ObjectArguments()).await() + .assertArrayEquals(true, message = "Block was placed") + } + thenIdle(1) + thenExecute { helper.assertBlockHas(BlockPos(1, 2, 3), BlockMonitor.STATE, MonitorEdgeState.LR) } + } + + /** + * Checks turtles can place into compostors. These are non-typical inventories, so + * worth testing. + */ + @GameTest + fun Use_compostors(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + getApi().dropDown(Optional.empty()).await() + .assertArrayEquals(true, message = "Item was dropped") + assertEquals(63, getApi().getItemCount(Optional.of(1)), "Only dropped one item") + } + } + + /** + * Checks turtles can be cleaned in cauldrons. + * + * Currently not required as turtles can no longer right-click cauldrons. + */ + @GameTest(required = false) + fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { + thenOnComputer { + val details = getTurtleItemDetail(1, true) + getApi().place(ObjectArguments()).await() + .assertArrayEquals(true, message = "Used item on cauldron") + val newDetails = getTurtleItemDetail(1, true) + + assertEquals("computercraft:turtle_normal", newDetails["name"], "Still a turtle") + assertNotEquals(details["nbt"], newDetails["nbt"], "Colour should have changed") + } + } + + /** + * Checks turtles can use IDetailProviders by getting details for a printed page. + */ + @GameTest + fun Item_detail_provider(helper: GameTestHelper) = helper.sequence { + // Register a dummy provider for printout items + thenExecute { + DetailRegistries.ITEM_STACK.addProvider( + object : + BasicItemDetailProvider("printout", ItemPrintout::class.java) { + override fun provideDetails(data: MutableMap, stack: ItemStack, item: ItemPrintout) { + data["type"] = item.type.toString().lowercase() + } + }, + ) + } + thenOnComputer { + val details = getTurtleItemDetail(detailed = true) + assertEquals(mapOf("type" to "page"), details["printout"]) { + "Printout information is returned (whole map is $details)" + } + } + } + + /** + * Advanced turtles resist all explosions but normal ones don't. + */ + @GameTest + fun Resists_explosions(helper: GameTestHelper) = helper.sequence { + thenExecute { + val pos = helper.absolutePos(BlockPos(2, 2, 2)) + val tnt = PrimedTnt(helper.level, pos.x + 0.5, pos.y + 1.0, pos.z + 0.5, null) + tnt.fuse = 1 + helper.level.addFreshEntity(tnt) + } + thenWaitUntil { helper.assertEntityNotPresent(EntityType.TNT) } + thenExecute { + helper.assertBlockPresent(Registry.ModBlocks.TURTLE_ADVANCED.get(), BlockPos(2, 2, 2)) + helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 1)) + } + } + + /** + * Turtles resist mob explosions + */ + @GameTest + fun Resists_entity_explosions(helper: GameTestHelper) = helper.sequence { + thenExecute { helper.getEntity(EntityType.CREEPER).ignite() } + thenWaitUntil { helper.assertEntityNotPresent(EntityType.CREEPER) } + thenExecute { + helper.assertBlockPresent(Registry.ModBlocks.TURTLE_ADVANCED.get(), BlockPos(2, 2, 2)) + helper.assertBlockPresent(Registry.ModBlocks.TURTLE_NORMAL.get(), BlockPos(2, 2, 1)) + } + } + + /** + * Test calling `turtle.drop` into an inventory. + */ + @GameTest + fun Drop_to_chest(helper: GameTestHelper) = helper.sequence { + val turtle = BlockPos(2, 2, 2) + val chest = BlockPos(2, 2, 3) + + thenOnComputer { + getApi().drop(Optional.of(32)).await() + .assertArrayEquals(true, message = "Could not drop items") + } + thenExecute { + helper.assertContainerExactly(turtle, listOf(ItemStack(Blocks.DIRT, 32), ItemStack.EMPTY, ItemStack(Blocks.DIRT, 32))) + helper.assertContainerExactly(chest, listOf(ItemStack(Blocks.DIRT, 48))) + } + } + + // TODO: Ghost peripherals? + // TODO: Dropping into minecarts + // TODO: Turtle sucking from items +} + +private suspend fun LuaTaskContext.getTurtleItemDetail(slot: Int = 1, detailed: Boolean = false): Map { + val item = getApi().getItemDetail(context, Optional.of(slot), Optional.of(detailed)).await() + assertThat("Returns details", item, array(instanceOf(Map::class.java))) + + @Suppress("UNCHECKED_CAST") + return item!![0] as Map +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt b/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt new file mode 100644 index 000000000..e492f8fe0 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt @@ -0,0 +1,222 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest.api + +import dan200.computercraft.gametest.core.ManagedComputers +import dan200.computercraft.mixin.gametest.GameTestHelperAccessor +import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor +import dan200.computercraft.test.core.computer.LuaTaskContext +import net.minecraft.commands.arguments.blocks.BlockInput +import net.minecraft.core.BlockPos +import net.minecraft.gametest.framework.* +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.Container +import net.minecraft.world.entity.Entity +import net.minecraft.world.entity.EntityType +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.block.state.properties.Property +import net.minecraftforge.registries.ForgeRegistries + +/** + * Globally usable structures. + * + * @see GameTest.template + */ +object Structures { + /** The "default" structure, a 5x5 area with a polished Andesite floor */ + const val DEFAULT = "default" +} + +/** Pre-set in-game times */ +object Times { + const val NOON: Long = 6000 +} + +/** + * Custom timeouts for various test types. + * + * @see GameTest.timeoutTicks + */ +object Timeouts { + private const val SECOND: Int = 20 + + const val COMPUTER_TIMEOUT: Int = SECOND * 15 +} + +/** + * Equivalent to [GameTestSequence.thenExecute], but which won't run the next steps if the parent fails. + */ +fun GameTestSequence.thenExecuteFailFast(task: Runnable): GameTestSequence = + thenExecute(task).thenWaitUntil { + val failure = (this as GameTestSequenceAccessor).parent.error + if (failure != null) throw failure + } + +/** + * Wait until a computer has finished running and check it is OK. + */ +fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = ComputerState.DONE): GameTestSequence { + val label = (this as GameTestSequenceAccessor).parent.testName + (if (name == null) "" else ".$name") + + thenWaitUntil { + val computer = ComputerState.get(label) + if (computer == null || !computer.isDone(marker)) throw GameTestAssertException("Computer '$label' has not reached $marker yet.") + } + thenExecuteFailFast { ComputerState.get(label)!!.check(marker) } + return this +} + +/** + * Run a task on a computer but don't wait for it to finish. + */ +fun GameTestSequence.thenStartComputer(name: String? = null, action: suspend LuaTaskContext.() -> Unit): GameTestSequence { + val test = (this as GameTestSequenceAccessor).parent + val label = test.testName + (if (name == null) "" else ".$name") + return thenExecuteFailFast { ManagedComputers.enqueue(test, label, action) } +} + +/** + * Run a task on a computer and wait for it to finish. + */ +fun GameTestSequence.thenOnComputer(name: String? = null, action: suspend LuaTaskContext.() -> Unit): GameTestSequence { + val test = (this as GameTestSequenceAccessor).parent + val label = test.testName + (if (name == null) "" else ".$name") + var monitor: ManagedComputers.Monitor? = null + thenExecuteFailFast { monitor = ManagedComputers.enqueue(test, label, action) } + thenWaitUntil { if (!monitor!!.isFinished) throw GameTestAssertException("Computer '$label' has not finished yet.") } + thenExecuteFailFast { monitor!!.check() } + return this +} + +/** + * Create a new game test sequence + */ +fun GameTestHelper.sequence(run: GameTestSequence.() -> Unit) { + val sequence = startSequence() + run(sequence) + sequence.thenSucceed() +} + +/** + * A custom instance of [GameTestAssertPosException] which allows for longer error messages. + */ +private class VerboseGameTestAssertPosException(message: String, absolutePos: BlockPos, relativePos: BlockPos, tick: Long) : + GameTestAssertPosException(message, absolutePos, relativePos, tick) { + override fun getMessageToShowAtBlock(): String = message!!.lineSequence().first() +} + +/** + * Fail this test. Unlike [GameTestHelper.fail], this trims the in-game error message to the first line. + */ +private fun GameTestHelper.failVerbose(message: String, pos: BlockPos): Nothing { + throw VerboseGameTestAssertPosException(message, absolutePos(pos), pos, tick) +} + +/** Fail with an optional context message. */ +private fun GameTestHelper.fail(message: String?, detail: String, pos: BlockPos): Nothing { + failVerbose(if (message.isNullOrEmpty()) detail else "$message: $detail", pos) +} + +/** + * A version of [GameTestHelper.assertBlockState] which also includes the current block state. + */ +fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String = "") { + val state = getBlockState(pos) + if (!predicate(state)) fail(message, state.toString(), pos) +} + +/** + * A version of [GameTestHelper.assertBlockProperty] which includes the current block state in the error message. + */ +fun > GameTestHelper.assertBlockHas(pos: BlockPos, property: Property, value: T, message: String = "") { + val state = getBlockState(pos) + if (!state.hasProperty(property)) { + val id = ForgeRegistries.BLOCKS.getKey(state.block) + fail(message, "block $id does not have property ${property.name}", pos) + } else if (state.getValue(property) != value) { + fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos) + } +} + +/** + * Assert a container contains exactly these items and no more. + * + * @param pos The position of the container. + * @param items The list of items this container must contain. This should be equal to the expected contents of the + * first `n` slots - the remaining are required to be empty. + */ +fun GameTestHelper.assertContainerExactly(pos: BlockPos, items: List) { + val container = getBlockEntity(pos) ?: failVerbose("Expected a container at $pos, found nothing", pos) + if (container !is Container) { + failVerbose("Expected a container at $pos, found ${getName(container.type)}", pos) + } + + val slot = (0 until container.containerSize).indexOfFirst { slot -> + val expected = if (slot >= items.size) ItemStack.EMPTY else items[slot] + !ItemStack.matches(container.getItem(slot), expected) + } + + if (slot >= 0) { + failVerbose( + """ + Items do not match (first mismatch at slot $slot). + Expected: $items + Container: ${(0 until container.containerSize).map { container.getItem(it) }.dropLastWhile { it.isEmpty }} + """.trimIndent(), + pos, + ) + } +} + +private fun getName(type: BlockEntityType<*>): ResourceLocation = ForgeRegistries.BLOCK_ENTITIES.getKey(type)!! + +/** + * Get a [BlockEntity] of a specific type. + */ +fun GameTestHelper.getBlockEntity(pos: BlockPos, type: BlockEntityType): T { + val tile = getBlockEntity(pos) + @Suppress("UNCHECKED_CAST") + return when { + tile == null -> failVerbose("Expected ${getName(type)}, but no tile was there", pos) + tile.type != type -> failVerbose("Expected ${getName(type)} but got ${getName(tile.type)}", pos) + else -> tile as T + } +} + +/** + * Get all entities of a specific type within the test structure. + */ +fun GameTestHelper.getEntities(type: EntityType): List { + val info = (this as GameTestHelperAccessor).testInfo + return level.getEntities(type, info.structureBounds!!) { it.isAlive } +} + +/** + * Get an [Entity] inside the game structure, requiring there to be a single one. + */ +fun GameTestHelper.getEntity(type: EntityType): T { + val entities = getEntities(type) + when (entities.size) { + 0 -> throw GameTestAssertException("No $type entities") + 1 -> return entities[0] + else -> throw GameTestAssertException("Multiple $type entities (${entities.size} in bounding box)") + } +} + +/** + * Set a block within the test structure. + */ +fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3) + +/** + * Modify a block state within the test. + */ +fun GameTestHelper.modifyBlock(pos: BlockPos, modify: (BlockState) -> BlockState) { + setBlock(pos, modify(getBlockState(pos))) +} diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt b/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt new file mode 100644 index 000000000..624c98ea7 --- /dev/null +++ b/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt @@ -0,0 +1,137 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.gametest.core + +import dan200.computercraft.api.lua.ILuaAPI +import dan200.computercraft.core.apis.OSAPI +import dan200.computercraft.core.lua.CobaltLuaMachine +import dan200.computercraft.core.lua.ILuaMachine +import dan200.computercraft.core.lua.MachineEnvironment +import dan200.computercraft.core.lua.MachineResult +import dan200.computercraft.gametest.api.thenOnComputer +import dan200.computercraft.mixin.gametest.GameTestInfoAccessor +import dan200.computercraft.shared.computer.core.ServerContext +import dan200.computercraft.test.core.computer.KotlinLuaMachine +import dan200.computercraft.test.core.computer.LuaTaskContext +import net.minecraft.gametest.framework.GameTestAssertException +import net.minecraft.gametest.framework.GameTestAssertPosException +import net.minecraft.gametest.framework.GameTestInfo +import net.minecraft.gametest.framework.GameTestSequence +import org.apache.logging.log4j.LogManager +import java.io.InputStream +import java.util.* +import java.util.concurrent.ConcurrentLinkedDeque +import java.util.concurrent.atomic.AtomicReference + +/** + * Provides a custom [ILuaMachine] which allows computers to run Kotlin or Lua code, depending on their ID. + * + * This allows writing game tests which consume Lua APIs, without having the overhead of starting a new computer for + * each test. + * + * @see GameTestSequence.thenOnComputer + */ +object ManagedComputers : ILuaMachine.Factory { + private val LOGGER = LogManager.getLogger(ManagedComputers::class.java) + private val computers: MutableMap Unit>> = mutableMapOf() + + internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor { + val monitor = Monitor(test, label) + computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add { + try { + LOGGER.info("Running $label") + task() + monitor.result.set(Result.success(Unit)) + } catch (e: Throwable) { + if (e !is AssertionError) LOGGER.error("Computer $label failed", e) + monitor.result.set(Result.failure(e)) + throw e + } finally { + LOGGER.info("Finished $label") + } + } + + ServerContext.get(test.level.server).registry().computers + .firstOrNull { it.label == label }?.queueEvent("test_wakeup") + + return monitor + } + + override fun create(environment: MachineEnvironment): ILuaMachine = DelegateMachine(environment) + + private class DelegateMachine(private val environment: MachineEnvironment) : ILuaMachine { + private val apis = mutableListOf() + private var delegate: ILuaMachine? = null + + override fun addAPI(api: ILuaAPI) { + val delegate = this.delegate + if (delegate != null) return delegate.addAPI(api) + + apis.add(api) + + if (api is OSAPI) { + val newMachine = if (api.computerID != 1) { + CobaltLuaMachine(environment) + } else if (api.computerLabel != null) { + KotlinMachine(environment, api.computerLabel[0] as String) + } else { + LOGGER.error("Kotlin Lua machine must have a label") + CobaltLuaMachine(environment) + } + + this.delegate = newMachine + for (api in apis) newMachine.addAPI(api) + } + } + + override fun loadBios(bios: InputStream): MachineResult { + val delegate = this.delegate ?: return MachineResult.error("Computer not created") + return delegate.loadBios(bios) + } + + override fun handleEvent(eventName: String?, arguments: Array?): MachineResult { + val delegate = this.delegate ?: return MachineResult.error("Computer not created") + return delegate.handleEvent(eventName, arguments) + } + + override fun printExecutionState(out: StringBuilder) { + delegate?.printExecutionState(out) + } + + override fun close() { + delegate?.close() + } + } + + private class KotlinMachine(environment: MachineEnvironment, private val label: String) : KotlinLuaMachine(environment) { + override fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? = computers[label]?.poll() + } + + class Monitor(private val test: GameTestInfo, private val label: String) { + internal val result = AtomicReference>() + + val isFinished + get() = result.get() != null + + fun check() { + val result = result.get() ?: fail("Computer $label did not finish") + val error = result.exceptionOrNull() + if (error != null) fail(error.message ?: error.toString()) + } + + private fun fail(message: String): Nothing { + val computer = + ServerContext.get(test.level.server).registry().computers.firstOrNull { it.label == label } + if (computer == null) { + throw GameTestAssertException(message) + } else { + val pos = computer.position + val relativePos = pos.subtract(test.structureBlockPos) + throw GameTestAssertPosException(message, pos, relativePos, (test as GameTestInfoAccessor).`computercraft$getTick`()) + } + } + } +} diff --git a/src/testMod/resources/META-INF/accesstransformer.cfg b/src/testMod/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index 6d0f7e462..000000000 --- a/src/testMod/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,6 +0,0 @@ -public net.minecraft.gametest.framework.TestCommand m_128010_(Lnet/minecraft/commands/CommandSourceStack;Ljava/lang/String;)I # exportTestStructure - -public net.minecraft.gametest.framework.GameTestHelper m_177448_()Lnet/minecraft/world/phys/AABB; # getBounds -public net.minecraft.gametest.framework.GameTestHelper f_127595_ # testInfo - -public net.minecraft.gametest.framework.GameTestSequence f_127774_ # parent diff --git a/src/testMod/resources/META-INF/mods.toml b/src/testMod/resources/META-INF/mods.toml index 30ddf13c4..f9ba9dc8a 100644 --- a/src/testMod/resources/META-INF/mods.toml +++ b/src/testMod/resources/META-INF/mods.toml @@ -16,3 +16,10 @@ displayName="CC: Tweaked test framework" description=''' A test framework for ensuring CC: Tweaked works correctly. ''' + +[[dependencies.cctest]] +modId="computercraft" +mandatory=true +versionRange="[1.0,)" +ordering="AFTER" +side="BOTH" diff --git a/src/testMod/resources/computercraft-gametest.mixins.json b/src/testMod/resources/computercraft-gametest.mixins.json new file mode 100644 index 000000000..76ac77997 --- /dev/null +++ b/src/testMod/resources/computercraft-gametest.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "package": "dan200.computercraft.mixin.gametest", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + }, + "mixins": [ + "GameTestHelperAccessor", + "GameTestInfoAccessor", + "GameTestSequenceAccessor", + "GameTestSequenceMixin", + "TestCommandAccessor" + ] +} diff --git a/src/testMod/server-files/computers/computer/0/startup.lua b/src/testMod/resources/data/cctest/computer/startup.lua similarity index 100% rename from src/testMod/server-files/computers/computer/0/startup.lua rename to src/testMod/resources/data/cctest/computer/startup.lua diff --git a/src/testMod/server-files/computers/computer/0/tests/craftos_test.sends_basic_rednet_messages.echo.lua b/src/testMod/resources/data/cctest/computer/tests/craftos_test.sends_basic_rednet_messages.echo.lua similarity index 100% rename from src/testMod/server-files/computers/computer/0/tests/craftos_test.sends_basic_rednet_messages.echo.lua rename to src/testMod/resources/data/cctest/computer/tests/craftos_test.sends_basic_rednet_messages.echo.lua diff --git a/src/testMod/server-files/computers/computer/0/tests/craftos_test.sends_basic_rednet_messages.main.lua b/src/testMod/resources/data/cctest/computer/tests/craftos_test.sends_basic_rednet_messages.main.lua similarity index 100% rename from src/testMod/server-files/computers/computer/0/tests/craftos_test.sends_basic_rednet_messages.main.lua rename to src/testMod/resources/data/cctest/computer/tests/craftos_test.sends_basic_rednet_messages.main.lua diff --git a/src/testMod/server-files/structures/computer_test.no_through_signal.snbt b/src/testMod/resources/data/cctest/structures/computer_test.no_through_signal.snbt similarity index 100% rename from src/testMod/server-files/structures/computer_test.no_through_signal.snbt rename to src/testMod/resources/data/cctest/structures/computer_test.no_through_signal.snbt diff --git a/src/testMod/resources/data/cctest/structures/computer_test.no_through_signal_reverse.snbt b/src/testMod/resources/data/cctest/structures/computer_test.no_through_signal_reverse.snbt new file mode 100644 index 000000000..592ccefaa --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/computer_test.no_through_signal_reverse.snbt @@ -0,0 +1,141 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:lever{face:floor,facing:south,powered:false}"}, + {pos: [2, 1, 1], state: "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}"}, + {pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:off}", nbt: {ComputerId: 0, Label: "computer_test.no_through_signal_rev", On: 0b, id: "computercraft:computer_advanced"}}, + {pos: [2, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, + {pos: [2, 1, 4], state: "minecraft:redstone_lamp{lit:false}"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:redstone_lamp{lit:false}", + "minecraft:air", + "minecraft:lever{face:floor,facing:south,powered:false}", + "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}", + "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", + "computercraft:computer_advanced{facing:north,state:off}" + ] +} diff --git a/src/testMod/resources/data/cctest/structures/computer_test.set_and_destroy.snbt b/src/testMod/resources/data/cctest/structures/computer_test.set_and_destroy.snbt new file mode 100644 index 000000000..c9dcb29c8 --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/computer_test.set_and_destroy.snbt @@ -0,0 +1,138 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "computer_test.set_and_destroy", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [2, 1, 3], state: "minecraft:redstone_lamp{lit:false}"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:redstone_lamp{lit:false}", + "minecraft:air", + "computercraft:computer_advanced{facing:north,state:on}" + ] +} diff --git a/src/testMod/server-files/structures/craftos_test.sends_basic_rednet_messages.snbt b/src/testMod/resources/data/cctest/structures/craftos_test.sends_basic_rednet_messages.snbt similarity index 100% rename from src/testMod/server-files/structures/craftos_test.sends_basic_rednet_messages.snbt rename to src/testMod/resources/data/cctest/structures/craftos_test.sends_basic_rednet_messages.snbt diff --git a/src/testMod/resources/data/cctest/structures/default.snbt b/src/testMod/resources/data/cctest/structures/default.snbt new file mode 100644 index 000000000..6bbbe27a5 --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/default.snbt @@ -0,0 +1,136 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "minecraft:air"}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:air" + ] +} diff --git a/src/testMod/server-files/structures/disk_drive_test.audio_disk.snbt b/src/testMod/resources/data/cctest/structures/disk_drive_test.audio_disk.snbt similarity index 96% rename from src/testMod/server-files/structures/disk_drive_test.audio_disk.snbt rename to src/testMod/resources/data/cctest/structures/disk_drive_test.audio_disk.snbt index ead09c8b3..95da26f01 100644 --- a/src/testMod/server-files/structures/disk_drive_test.audio_disk.snbt +++ b/src/testMod/resources/data/cctest/structures/disk_drive_test.audio_disk.snbt @@ -15,7 +15,7 @@ {pos: [0, 1, 1], state: "computercraft:disk_drive{facing:north,state:full}", nbt: {Item: {Count: 1b, id: "minecraft:music_disc_13"}, id: "computercraft:disk_drive"}}, {pos: [0, 1, 2], state: "minecraft:air"}, {pos: [1, 1, 0], state: "minecraft:air"}, - {pos: [1, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "disk_drive_test.audio_disk", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [1, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "disk_drive_test.audio_disk", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [1, 1, 2], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, {pos: [2, 1, 1], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/disk_drive_test.ejects_disk.snbt b/src/testMod/resources/data/cctest/structures/disk_drive_test.ejects_disk.snbt similarity index 99% rename from src/testMod/server-files/structures/disk_drive_test.ejects_disk.snbt rename to src/testMod/resources/data/cctest/structures/disk_drive_test.ejects_disk.snbt index 7453134b3..d3576a9c4 100644 --- a/src/testMod/server-files/structures/disk_drive_test.ejects_disk.snbt +++ b/src/testMod/resources/data/cctest/structures/disk_drive_test.ejects_disk.snbt @@ -43,7 +43,7 @@ {pos: [2, 1, 3], state: "minecraft:white_stained_glass"}, {pos: [2, 1, 4], state: "minecraft:air"}, {pos: [3, 1, 0], state: "minecraft:air"}, - {pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "disk_drive_test.ejects_disk", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "disk_drive_test.ejects_disk", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [3, 1, 2], state: "minecraft:white_stained_glass"}, {pos: [3, 1, 3], state: "minecraft:white_stained_glass"}, {pos: [3, 1, 4], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/modem_test.gains_peripherals.snbt b/src/testMod/resources/data/cctest/structures/modem_test.gains_peripherals.snbt similarity index 99% rename from src/testMod/server-files/structures/modem_test.gains_peripherals.snbt rename to src/testMod/resources/data/cctest/structures/modem_test.gains_peripherals.snbt index 6f91041bd..63e670a58 100644 --- a/src/testMod/server-files/structures/modem_test.gains_peripherals.snbt +++ b/src/testMod/resources/data/cctest/structures/modem_test.gains_peripherals.snbt @@ -48,7 +48,7 @@ {pos: [3, 1, 3], state: "minecraft:air"}, {pos: [3, 1, 4], state: "minecraft:air"}, {pos: [4, 1, 0], state: "minecraft:air"}, - {pos: [4, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.gains_peripherals", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [4, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.gains_peripherals", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [4, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}}, {pos: [4, 1, 3], state: "minecraft:air"}, {pos: [4, 1, 4], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/modem_test.have_peripherals.snbt b/src/testMod/resources/data/cctest/structures/modem_test.have_peripherals.snbt similarity index 99% rename from src/testMod/server-files/structures/modem_test.have_peripherals.snbt rename to src/testMod/resources/data/cctest/structures/modem_test.have_peripherals.snbt index d4f5457a2..c16283a27 100644 --- a/src/testMod/server-files/structures/modem_test.have_peripherals.snbt +++ b/src/testMod/resources/data/cctest/structures/modem_test.have_peripherals.snbt @@ -49,7 +49,7 @@ {pos: [3, 1, 4], state: "minecraft:air"}, {pos: [4, 1, 0], state: "minecraft:air"}, {pos: [4, 1, 1], state: "minecraft:air"}, - {pos: [4, 1, 2], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.have_peripherals", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [4, 1, 2], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.have_peripherals", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [4, 1, 3], state: "minecraft:air"}, {pos: [4, 1, 4], state: "minecraft:air"}, {pos: [0, 2, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/modem_test.transmits_messages.snbt b/src/testMod/resources/data/cctest/structures/modem_test.transmits_messages.snbt similarity index 98% rename from src/testMod/server-files/structures/modem_test.transmits_messages.snbt rename to src/testMod/resources/data/cctest/structures/modem_test.transmits_messages.snbt index b6431ba77..79ed20b01 100644 --- a/src/testMod/server-files/structures/modem_test.transmits_messages.snbt +++ b/src/testMod/resources/data/cctest/structures/modem_test.transmits_messages.snbt @@ -35,7 +35,7 @@ {pos: [1, 1, 0], state: "minecraft:air"}, {pos: [1, 1, 1], state: "minecraft:air"}, {pos: [1, 1, 2], state: "minecraft:air"}, - {pos: [1, 1, 3], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.transmits_messages.receive", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [1, 1, 3], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.transmits_messages.receive", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [1, 1, 4], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, {pos: [2, 1, 1], state: "computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}}, @@ -43,7 +43,7 @@ {pos: [2, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_off,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}}, {pos: [2, 1, 4], state: "minecraft:air"}, {pos: [3, 1, 0], state: "minecraft:air"}, - {pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.transmits_messages.send", On: 1b, id: "computercraft:computer_advanced"}}, + {pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.transmits_messages.send", On: 1b, id: "computercraft:computer_advanced"}}, {pos: [3, 1, 2], state: "minecraft:air"}, {pos: [3, 1, 3], state: "minecraft:air"}, {pos: [3, 1, 4], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/monitor_test.ensures_valid_on_place.snbt b/src/testMod/resources/data/cctest/structures/monitor_test.ensures_valid_on_place.snbt similarity index 100% rename from src/testMod/server-files/structures/monitor_test.ensures_valid_on_place.snbt rename to src/testMod/resources/data/cctest/structures/monitor_test.ensures_valid_on_place.snbt diff --git a/src/testMod/server-files/structures/monitor_test.looks_acceptable.snbt b/src/testMod/resources/data/cctest/structures/monitor_test.looks_acceptable.snbt similarity index 100% rename from src/testMod/server-files/structures/monitor_test.looks_acceptable.snbt rename to src/testMod/resources/data/cctest/structures/monitor_test.looks_acceptable.snbt diff --git a/src/testMod/server-files/structures/monitor_test.looks_acceptable_dark.snbt b/src/testMod/resources/data/cctest/structures/monitor_test.looks_acceptable_dark.snbt similarity index 100% rename from src/testMod/server-files/structures/monitor_test.looks_acceptable_dark.snbt rename to src/testMod/resources/data/cctest/structures/monitor_test.looks_acceptable_dark.snbt diff --git a/src/testMod/server-files/structures/printouttest.in_frame_at_night.snbt b/src/testMod/resources/data/cctest/structures/printouttest.in_frame_at_night.snbt similarity index 100% rename from src/testMod/server-files/structures/printouttest.in_frame_at_night.snbt rename to src/testMod/resources/data/cctest/structures/printouttest.in_frame_at_night.snbt diff --git a/src/testMod/server-files/structures/turtle_test.cleaned_with_cauldrons.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.cleaned_with_cauldrons.snbt similarity index 97% rename from src/testMod/server-files/structures/turtle_test.cleaned_with_cauldrons.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.cleaned_with_cauldrons.snbt index 7a30e1677..a208024a5 100644 --- a/src/testMod/server-files/structures/turtle_test.cleaned_with_cauldrons.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.cleaned_with_cauldrons.snbt @@ -14,7 +14,7 @@ {pos: [0, 1, 0], state: "minecraft:air"}, {pos: [0, 1, 1], state: "minecraft:air"}, {pos: [0, 1, 2], state: "minecraft:air"}, - {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 0, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "turtle_test.cleaned_with_cauldrons", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 0, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "turtle_test.cleaned_with_cauldrons", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [1, 1, 1], state: "minecraft:cauldron{level:3}"}, {pos: [1, 1, 2], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, diff --git a/src/testMod/resources/data/cctest/structures/turtle_test.drop_to_chest.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.drop_to_chest.snbt new file mode 100644 index 000000000..669efdf6b --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/turtle_test.drop_to_chest.snbt @@ -0,0 +1,138 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:dirt"}, {Count: 32b, Slot: 2b, id: "minecraft:dirt"}], Label: "turtle_test.drop_to_chest", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 3], state: "minecraft:chest{facing:west,type:single,waterlogged:false}", nbt: {Items: [{Count: 16b, Slot: 0b, id: "minecraft:dirt"}], id: "minecraft:chest"}}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:air", + "computercraft:turtle_normal{facing:south,waterlogged:false}", + "minecraft:chest{facing:west,type:single,waterlogged:false}" + ] +} diff --git a/src/testMod/server-files/structures/turtle_test.gather_lava.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.gather_lava.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.gather_lava.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.gather_lava.snbt index 102071219..e2e703e88 100644 --- a/src/testMod/server-files/structures/turtle_test.gather_lava.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.gather_lava.snbt @@ -64,7 +64,7 @@ {pos: [1, 2, 4], state: "minecraft:air"}, {pos: [2, 2, 0], state: "minecraft:air"}, {pos: [2, 2, 1], state: "minecraft:air"}, - {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:bucket"}], Label: "turtle_test.gather_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:bucket"}], Label: "turtle_test.gather_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 2, 3], state: "minecraft:air"}, {pos: [2, 2, 4], state: "minecraft:air"}, {pos: [3, 2, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.hoe_dirt.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.hoe_dirt.snbt similarity index 96% rename from src/testMod/server-files/structures/turtle_test.hoe_dirt.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.hoe_dirt.snbt index d662d1b6c..4b43c47b3 100644 --- a/src/testMod/server-files/structures/turtle_test.hoe_dirt.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.hoe_dirt.snbt @@ -14,7 +14,7 @@ {pos: [0, 1, 0], state: "minecraft:air"}, {pos: [0, 1, 1], state: "minecraft:air"}, {pos: [0, 1, 2], state: "minecraft:air"}, - {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [], Label: "turtle_test.hoe_dirt", LeftUpgrade: "minecraft:diamond_hoe", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.hoe_dirt", LeftUpgrade: "minecraft:diamond_hoe", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [1, 1, 1], state: "minecraft:dirt"}, {pos: [1, 1, 2], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.item_detail_provider.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.item_detail_provider.snbt similarity index 98% rename from src/testMod/server-files/structures/turtle_test.item_detail_provider.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.item_detail_provider.snbt index 0c8647206..5e67a701f 100644 --- a/src/testMod/server-files/structures/turtle_test.item_detail_provider.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.item_detail_provider.snbt @@ -14,7 +14,7 @@ {pos: [0, 1, 0], state: "minecraft:air"}, {pos: [0, 1, 1], state: "minecraft:air"}, {pos: [0, 1, 2], state: "minecraft:air"}, - {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:printed_page", tag: {Color0: "fffffffffffffffffffffffff", Color1: "fffffffffffffffffffffffff", Color10: "fffffffffffffffffffffffff", Color11: "fffffffffffffffffffffffff", Color12: "fffffffffffffffffffffffff", Color13: "fffffffffffffffffffffffff", Color14: "fffffffffffffffffffffffff", Color15: "fffffffffffffffffffffffff", Color16: "fffffffffffffffffffffffff", Color17: "fffffffffffffffffffffffff", Color18: "fffffffffffffffffffffffff", Color19: "fffffffffffffffffffffffff", Color2: "fffffffffffffffffffffffff", Color20: "fffffffffffffffffffffffff", Color3: "fffffffffffffffffffffffff", Color4: "fffffffffffffffffffffffff", Color5: "fffffffffffffffffffffffff", Color6: "fffffffffffffffffffffffff", Color7: "fffffffffffffffffffffffff", Color8: "fffffffffffffffffffffffff", Color9: "fffffffffffffffffffffffff", Pages: 1, Text0: "Example ", Text1: " ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "Example page"}}], Label: "turtle_test.item_detail_provider", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:printed_page", tag: {Color0: "fffffffffffffffffffffffff", Color1: "fffffffffffffffffffffffff", Color10: "fffffffffffffffffffffffff", Color11: "fffffffffffffffffffffffff", Color12: "fffffffffffffffffffffffff", Color13: "fffffffffffffffffffffffff", Color14: "fffffffffffffffffffffffff", Color15: "fffffffffffffffffffffffff", Color16: "fffffffffffffffffffffffff", Color17: "fffffffffffffffffffffffff", Color18: "fffffffffffffffffffffffff", Color19: "fffffffffffffffffffffffff", Color2: "fffffffffffffffffffffffff", Color20: "fffffffffffffffffffffffff", Color3: "fffffffffffffffffffffffff", Color4: "fffffffffffffffffffffffff", Color5: "fffffffffffffffffffffffff", Color6: "fffffffffffffffffffffffff", Color7: "fffffffffffffffffffffffff", Color8: "fffffffffffffffffffffffff", Color9: "fffffffffffffffffffffffff", Pages: 1, Text0: "Example ", Text1: " ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "Example page"}}], Label: "turtle_test.item_detail_provider", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [1, 1, 1], state: "minecraft:air"}, {pos: [1, 1, 2], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.place_lava.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.place_lava.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.place_lava.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.place_lava.snbt index 68cd47551..c93eb769e 100644 --- a/src/testMod/server-files/structures/turtle_test.place_lava.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.place_lava.snbt @@ -64,7 +64,7 @@ {pos: [1, 2, 4], state: "minecraft:air"}, {pos: [2, 2, 0], state: "minecraft:air"}, {pos: [2, 2, 1], state: "minecraft:air"}, - {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:lava_bucket"}], Label: "turtle_test.place_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:lava_bucket"}], Label: "turtle_test.place_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 2, 3], state: "minecraft:air"}, {pos: [2, 2, 4], state: "minecraft:air"}, {pos: [3, 2, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.place_monitor.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.place_monitor.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.place_monitor.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.place_monitor.snbt index 504fe1f1e..a1621f782 100644 --- a/src/testMod/server-files/structures/turtle_test.place_monitor.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.place_monitor.snbt @@ -34,7 +34,7 @@ {pos: [0, 1, 4], state: "minecraft:air"}, {pos: [1, 1, 0], state: "minecraft:air"}, {pos: [1, 1, 1], state: "minecraft:air"}, - {pos: [1, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:monitor_advanced"}], Label: "turtle_test.place_monitor", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:monitor_advanced"}], Label: "turtle_test.place_monitor", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [1, 1, 3], state: "minecraft:air"}, {pos: [1, 1, 4], state: "minecraft:dark_oak_planks"}, {pos: [2, 1, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.place_waterlogged.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.place_waterlogged.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.place_waterlogged.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.place_waterlogged.snbt index 609bb63b8..434b3d26f 100644 --- a/src/testMod/server-files/structures/turtle_test.place_waterlogged.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.place_waterlogged.snbt @@ -38,7 +38,7 @@ {pos: [1, 1, 3], state: "minecraft:white_stained_glass"}, {pos: [1, 1, 4], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:white_stained_glass"}, - {pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:true}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:oak_fence"}], Label: "turtle_test.place_waterlogged", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:true}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:oak_fence"}], Label: "turtle_test.place_waterlogged", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 1, 2], state: "minecraft:water{level:0}"}, {pos: [2, 1, 3], state: "minecraft:white_stained_glass"}, {pos: [2, 1, 4], state: "minecraft:air"}, diff --git a/src/testMod/resources/data/cctest/structures/turtle_test.resists_entity_explosions.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.resists_entity_explosions.snbt new file mode 100644 index 000000000..79b2f8ef6 --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/turtle_test.resists_entity_explosions.snbt @@ -0,0 +1,140 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 2], state: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [ + {blockPos: [1, 1, 2], pos: [1.5d, 1.0d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.0d, Name: "forge:step_height_addition"}, {Base: 0.25d, Name: "minecraft:generic.movement_speed"}, {Base: 0.08d, Name: "forge:entity_gravity"}, {Base: 16.0d, Modifiers: [{Amount: -0.0871987524284032d, Name: "Random spawn bonus", Operation: 1, UUID: [I; -1592956383, -599506679, -1812844190, 1076877318]}], Name: "minecraft:generic.follow_range"}], Brain: {memories: {}}, CanPickUpLoot: 0b, CanUpdate: 1b, DeathTime: 0s, ExplosionRadius: 3b, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, Fuse: 30s, HandDropChances: [0.085f, 0.085f], HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invulnerable: 0b, LeftHanded: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], OnGround: 1b, PersistenceRequired: 0b, PortalCooldown: 0, Pos: [2.5d, -58.0d, 3.5d], Rotation: [143.85756f, 0.0f], UUID: [I; 1463798444, -662876850, -1423329658, 948503391], id: "minecraft:creeper", ignited: 0b}} + ], + palette: [ + "minecraft:polished_andesite", + "minecraft:air", + "computercraft:turtle_normal{facing:south,waterlogged:false}", + "computercraft:turtle_advanced{facing:south,waterlogged:false}" + ] +} diff --git a/src/testMod/resources/data/cctest/structures/turtle_test.resists_explosions.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.resists_explosions.snbt new file mode 100644 index 000000000..89ae4bc14 --- /dev/null +++ b/src/testMod/resources/data/cctest/structures/turtle_test.resists_explosions.snbt @@ -0,0 +1,139 @@ +{ + DataVersion: 3120, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:barrier"}, + {pos: [0, 1, 1], state: "minecraft:barrier"}, + {pos: [0, 1, 2], state: "minecraft:barrier"}, + {pos: [0, 1, 3], state: "minecraft:barrier"}, + {pos: [0, 1, 4], state: "minecraft:barrier"}, + {pos: [1, 1, 0], state: "minecraft:barrier"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:barrier"}, + {pos: [2, 1, 0], state: "minecraft:barrier"}, + {pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 2], state: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 4], state: "minecraft:barrier"}, + {pos: [3, 1, 0], state: "minecraft:barrier"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:barrier"}, + {pos: [4, 1, 0], state: "minecraft:barrier"}, + {pos: [4, 1, 1], state: "minecraft:barrier"}, + {pos: [4, 1, 2], state: "minecraft:barrier"}, + {pos: [4, 1, 3], state: "minecraft:barrier"}, + {pos: [4, 1, 4], state: "minecraft:barrier"}, + {pos: [0, 2, 0], state: "minecraft:barrier"}, + {pos: [0, 2, 1], state: "minecraft:barrier"}, + {pos: [0, 2, 2], state: "minecraft:barrier"}, + {pos: [0, 2, 3], state: "minecraft:barrier"}, + {pos: [0, 2, 4], state: "minecraft:barrier"}, + {pos: [1, 2, 0], state: "minecraft:barrier"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:barrier"}, + {pos: [2, 2, 0], state: "minecraft:barrier"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:barrier"}, + {pos: [3, 2, 0], state: "minecraft:barrier"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:barrier"}, + {pos: [4, 2, 0], state: "minecraft:barrier"}, + {pos: [4, 2, 1], state: "minecraft:barrier"}, + {pos: [4, 2, 2], state: "minecraft:barrier"}, + {pos: [4, 2, 3], state: "minecraft:barrier"}, + {pos: [4, 2, 4], state: "minecraft:barrier"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [], + palette: [ + "minecraft:polished_andesite", + "minecraft:barrier", + "minecraft:air", + "computercraft:turtle_normal{facing:south,waterlogged:false}", + "computercraft:turtle_advanced{facing:south,waterlogged:false}" + ] +} diff --git a/src/testMod/server-files/structures/turtle_test.shears_sheep.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.shears_sheep.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.shears_sheep.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.shears_sheep.snbt index 52fb223ae..4f6afef2c 100644 --- a/src/testMod/server-files/structures/turtle_test.shears_sheep.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.shears_sheep.snbt @@ -89,7 +89,7 @@ {pos: [1, 3, 4], state: "minecraft:air"}, {pos: [2, 3, 0], state: "minecraft:air"}, {pos: [2, 3, 1], state: "minecraft:air"}, - {pos: [2, 3, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:shears", tag: {Damage: 0}}], Label: "turtle_test.shears_sheep", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 3, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:shears", tag: {Damage: 0}}], Label: "turtle_test.shears_sheep", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 3, 3], state: "minecraft:air"}, {pos: [2, 3, 4], state: "minecraft:air"}, {pos: [3, 3, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.unequip_refreshes_peripheral.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.unequip_refreshes_peripheral.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.unequip_refreshes_peripheral.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.unequip_refreshes_peripheral.snbt index 068c40c83..de5d48438 100644 --- a/src/testMod/server-files/structures/turtle_test.unequip_refreshes_peripheral.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.unequip_refreshes_peripheral.snbt @@ -39,7 +39,7 @@ {pos: [1, 1, 4], state: "minecraft:air"}, {pos: [2, 1, 0], state: "minecraft:air"}, {pos: [2, 1, 1], state: "minecraft:air"}, - {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [], Label: "turtle_test.unequip_refreshes_peripheral", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, RightUpgrade: "computercraft:wireless_modem_normal", RightUpgradeNbt: {active: 0b}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.unequip_refreshes_peripheral", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, RightUpgrade: "computercraft:wireless_modem_normal", RightUpgradeNbt: {active: 0b}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 1, 3], state: "minecraft:air"}, {pos: [2, 1, 4], state: "minecraft:air"}, {pos: [3, 1, 0], state: "minecraft:air"}, diff --git a/src/testMod/server-files/structures/turtle_test.use_compostors.snbt b/src/testMod/resources/data/cctest/structures/turtle_test.use_compostors.snbt similarity index 99% rename from src/testMod/server-files/structures/turtle_test.use_compostors.snbt rename to src/testMod/resources/data/cctest/structures/turtle_test.use_compostors.snbt index 8c70e0090..7bf966662 100644 --- a/src/testMod/server-files/structures/turtle_test.use_compostors.snbt +++ b/src/testMod/resources/data/cctest/structures/turtle_test.use_compostors.snbt @@ -64,7 +64,7 @@ {pos: [1, 2, 4], state: "minecraft:air"}, {pos: [2, 2, 0], state: "minecraft:air"}, {pos: [2, 2, 1], state: "minecraft:air"}, - {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:spruce_sapling"}], Label: "turtle_test.use_compostors", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:spruce_sapling"}], Label: "turtle_test.use_compostors", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}}, {pos: [2, 2, 3], state: "minecraft:air"}, {pos: [2, 2, 4], state: "minecraft:air"}, {pos: [3, 2, 0], state: "minecraft:air"}, diff --git a/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json b/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json new file mode 100644 index 000000000..0e238db06 --- /dev/null +++ b/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json @@ -0,0 +1,20 @@ +{ + "pools": [ + { + "name": "main", + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "computercraft:treasure_disk", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": "{\"Title\": \"Demo disk\", \"SubPath\": \"demo\", \"Colour\": 15905331}" + } + ] + } + ] + } + ] +} diff --git a/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.audio_disk.lua b/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.audio_disk.lua deleted file mode 100644 index d48ce5fda..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.audio_disk.lua +++ /dev/null @@ -1,2 +0,0 @@ -test.eq(true, disk.hasAudio("right"), "Has audio") -test.eq("C418 - 13", disk.getAudioTitle("right"), "Audio title") diff --git a/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.ejects_disk.lua b/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.ejects_disk.lua deleted file mode 100644 index 510af2a81..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/disk_drive_test.ejects_disk.lua +++ /dev/null @@ -1 +0,0 @@ -disk.eject("right") diff --git a/src/testMod/server-files/computers/computer/0/tests/modem_test.gains_peripherals.lua b/src/testMod/server-files/computers/computer/0/tests/modem_test.gains_peripherals.lua deleted file mode 100644 index d7f8559ab..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/modem_test.gains_peripherals.lua +++ /dev/null @@ -1,18 +0,0 @@ -local function check_peripherals(expected, msg) - local peripherals = peripheral.getNames() - table.sort(peripherals) - - test.eq(table.concat(expected, ", "), table.concat(peripherals, ", "), msg) -end - -check_peripherals({"back"}, "Has no peripherals on startup") -test.ok("initial") - -os.pullEvent("peripheral") -sleep(0) - -check_peripherals({ - "back", - "monitor_1", - "printer_1", -}, "Gains new peripherals") diff --git a/src/testMod/server-files/computers/computer/0/tests/modem_test.have_peripherals.lua b/src/testMod/server-files/computers/computer/0/tests/modem_test.have_peripherals.lua deleted file mode 100644 index 4752250d2..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/modem_test.have_peripherals.lua +++ /dev/null @@ -1,12 +0,0 @@ -local function check_peripherals(expected, msg) - local peripherals = peripheral.getNames() - table.sort(peripherals) - - test.eq(table.concat(expected, ", "), table.concat(peripherals, ", "), msg) -end - -check_peripherals({ - "monitor_0", - "printer_0", - "right", -}, "Starts with peripherals") diff --git a/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.receive.lua b/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.receive.lua deleted file mode 100644 index 7a2daa82b..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.receive.lua +++ /dev/null @@ -1,10 +0,0 @@ -local modem = peripheral.find("modem") -modem.open(12) - - -local _, name, chan, reply, payload, distance = os.pullEvent("modem_message") -test.eq("left", name, "Modem name") -test.eq(12, chan, "Channel") -test.eq(34, reply, "Reply channel") -test.eq("Hello!", payload, "Payload") -test.eq(4, distance, "Distance") -- Why 4?! diff --git a/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.send.lua b/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.send.lua deleted file mode 100644 index 0d36bb1a0..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/modem_test.transmits_messages.send.lua +++ /dev/null @@ -1,5 +0,0 @@ -local modem = peripheral.find("modem") -while true do - modem.transmit(12, 34, "Hello!") - sleep(1) -end diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.cleaned_with_cauldrons.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.cleaned_with_cauldrons.lua deleted file mode 100644 index 7041fec9b..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.cleaned_with_cauldrons.lua +++ /dev/null @@ -1,7 +0,0 @@ -local old_details = turtle.getItemDetail(1, true) - -test.assert(turtle.place(), "Dyed turtle") - -local new_details = turtle.getItemDetail(1, true) -test.eq("computercraft:turtle_normal", new_details.name, "Still a turtle") -test.neq(old_details.nbt, new_details.nbt, "Colour has changed") diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.gather_lava.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.gather_lava.lua deleted file mode 100644 index 928989f45..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.gather_lava.lua +++ /dev/null @@ -1,7 +0,0 @@ -turtle.placeDown() - -local item = turtle.getItemDetail() -test.eq("minecraft:lava_bucket", item.name) - -local has_down, down = turtle.inspectDown() -test.eq(false, has_down, "Air below") diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.hoe_dirt.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.hoe_dirt.lua deleted file mode 100644 index 51dc70576..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.hoe_dirt.lua +++ /dev/null @@ -1,5 +0,0 @@ -test.assert(turtle.dig()) - -local has_block, block = turtle.inspect() -test.assert(has_block, "Has block") -test.eq("minecraft:farmland", block.name) diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.item_detail_provider.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.item_detail_provider.lua deleted file mode 100644 index 76f4749f1..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.item_detail_provider.lua +++ /dev/null @@ -1,7 +0,0 @@ -test.ok("initial") - -local details = turtle.getItemDetail(1, true) - -test.assert(details, "Has details") -test.assert(details.printout, "Has printout meta") -test.eq("PAGE", details.printout.type) diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_lava.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_lava.lua deleted file mode 100644 index 2ad6f9ee1..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_lava.lua +++ /dev/null @@ -1,5 +0,0 @@ -test.assert(turtle.placeDown()) - -local ok, down = turtle.inspectDown() -test.assert(ok, "Has below") -test.eq("minecraft:lava", down.name, "Is lava") diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_monitor.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_monitor.lua deleted file mode 100644 index e5ad6f0e6..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_monitor.lua +++ /dev/null @@ -1,6 +0,0 @@ -test.assert(turtle.place()) - -local has_block, block = turtle.inspect() -test.assert(has_block, "Has block") -test.eq("computercraft:monitor_advanced", block.name) -test.eq("lr", block.state.state) diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_waterlogged.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_waterlogged.lua deleted file mode 100644 index e55dadfbc..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.place_waterlogged.lua +++ /dev/null @@ -1,6 +0,0 @@ -test.assert(turtle.place()) - -local has_block, block = turtle.inspect() -test.eq(true, has_block, "Has block") -test.eq("minecraft:oak_fence", block.name) -test.eq(true, block.state.waterlogged) diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.shears_sheep.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.shears_sheep.lua deleted file mode 100644 index 8efbb30ab..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.shears_sheep.lua +++ /dev/null @@ -1,9 +0,0 @@ --- TurtleTest.`Shears sheep` - -turtle.placeDown() - -local item = turtle.getItemDetail(2) -if item == nil then test.fail("Got no item") end -test.eq("minecraft:white_wool", item.name) - -test.ok() diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.unequip_refreshes_peripheral.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.unequip_refreshes_peripheral.lua deleted file mode 100644 index 2814b1a44..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.unequip_refreshes_peripheral.lua +++ /dev/null @@ -1,3 +0,0 @@ -test.eq("modem", peripheral.getType("right"), "Starts with a modem") -turtle.equipRight() -test.eq("drive", peripheral.getType("right"), "Unequipping gives a drive") diff --git a/src/testMod/server-files/computers/computer/0/tests/turtle_test.use_compostors.lua b/src/testMod/server-files/computers/computer/0/tests/turtle_test.use_compostors.lua deleted file mode 100644 index 7a5f4b52b..000000000 --- a/src/testMod/server-files/computers/computer/0/tests/turtle_test.use_compostors.lua +++ /dev/null @@ -1 +0,0 @@ -test.eq(true, turtle.dropDown(), "Drop items into compostor") diff --git a/src/testMod/server-files/computers/ids.json b/src/testMod/server-files/computers/ids.json deleted file mode 100644 index 9c8f668d7..000000000 --- a/src/testMod/server-files/computers/ids.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "computer": 0, - "peripheral.monitor": 1, - "peripheral.printer": 1 -} diff --git a/src/testMod/server-files/eula.txt b/src/testMod/server-files/eula.txt deleted file mode 100644 index e6765d6c9..000000000 --- a/src/testMod/server-files/eula.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Automatically generated EULA. Please don't use this for a real server. -eula=true diff --git a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable.png b/src/testMod/server-files/screenshots/monitor_test.looks_acceptable.png deleted file mode 100644 index 0639191d2..000000000 Binary files a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable.png and /dev/null differ diff --git a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark.png b/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark.png deleted file mode 100644 index 1ce71f27f..000000000 Binary files a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark.png and /dev/null differ diff --git a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark_vbo.png b/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark_vbo.png deleted file mode 100644 index ba57924ab..000000000 Binary files a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_dark_vbo.png and /dev/null differ diff --git a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_vbo.png b/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_vbo.png deleted file mode 100644 index 9c7014984..000000000 Binary files a/src/testMod/server-files/screenshots/monitor_test.looks_acceptable_vbo.png and /dev/null differ diff --git a/src/testMod/server-files/screenshots/printouttest.in_frame_at_night.png b/src/testMod/server-files/screenshots/printouttest.in_frame_at_night.png deleted file mode 100644 index 6b6f5ad3b..000000000 Binary files a/src/testMod/server-files/screenshots/printouttest.in_frame_at_night.png and /dev/null differ diff --git a/src/testMod/server-files/server.properties b/src/testMod/server-files/server.properties deleted file mode 100644 index 60384302f..000000000 --- a/src/testMod/server-files/server.properties +++ /dev/null @@ -1,45 +0,0 @@ -# Minecraft server properties -allow-flight=false -allow-nether=true -broadcast-console-to-ops=true -broadcast-rcon-to-ops=true -difficulty=easy -enable-command-block=true -enable-query=false -enable-rcon=false -enforce-whitelist=false -force-gamemode=false -function-permission-level=2 -gamemode=creative -generate-structures=false -generator-settings= -hardcore=false -level-name=world -level-seed= -level-type=flat -max-build-height=256 -max-players=20 -max-tick-time=60000 -max-world-size=29999984 -motd=A testing server -network-compression-threshold=256 -online-mode=false -op-permission-level=4 -player-idle-timeout=0 -prevent-proxy-connections=false -pvp=true -query.port=25565 -rcon.password= -rcon.port=25575 -resource-pack= -resource-pack-sha1= -server-ip= -server-port=25565 -snooper-enabled=true -spawn-animals=true -spawn-monsters=true -spawn-npcs=true -spawn-protection=16 -use-native-transport=true -view-distance=10 -white-list=false