1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-02-18 20:10:05 +00:00

Merge branch 'mc-1.16.x' into mc-1.17.x

This commit is contained in:
Jonathan Coates 2021-08-20 21:47:34 +01:00
commit 0a8e427c61
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
93 changed files with 2209 additions and 1602 deletions

2
.gitattributes vendored
View File

@ -1,6 +1,6 @@
# Ignore changes in generated files
src/generated/resources/data/** linguist-generated
src/test/server-files/structures linguist-generated
src/testMod/server-files/structures linguist-generated
* text=auto

View File

@ -32,7 +32,7 @@ jobs:
run: |
./gradlew assemble || ./gradlew assemble
./gradlew downloadAssets || ./gradlew downloadAssets
./gradlew build
xvfb-run ./gradlew build
- name: Upload Jar
uses: actions/upload-artifact@v2
@ -40,6 +40,15 @@ jobs:
name: CC-Tweaked
path: build/libs
- name: Upload Screnshots
uses: actions/upload-artifact@v2
with:
name: Screenshots
path: test-files/client/screenshots
if-no-files-found: ignore
retention-days: 5
if: failure()
- name: Upload Coverage
uses: codecov/codecov-action@v1

View File

@ -1,40 +1,11 @@
# ![CC: Tweaked](doc/logo.png)
[![Current build status](https://github.com/SquidDev-CC/CC-Tweaked/workflows/Build/badge.svg)](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
[![Current build status](https://github.com/SquidDev-CC/CC-Tweaked/workflows/Build/badge.svg)](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)][CurseForge]
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
turtles and more to Minecraft.
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
much-beloved [ComputerCraft], it continues its legacy with better performance, stability, and a wealth of new features.
## What?
ComputerCraft has always held a fond place in my heart: it's the mod which really got me into Minecraft, and it's the
mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original
developers have had less time to work on the mod, and moved onto other projects and commitments.
CC: Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the
ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time.
## Features
CC: Tweaked contains all the features of the latest version of ComputerCraft, as well as numerous fixes, performance
improvements and several nifty additions. I'd recommend checking out [the releases page](https://github.com/SquidDev-CC/CC-Tweaked/releases)
to see the full set of changes, but here's a couple of the more interesting additions:
- Improvements to the `http` library, including websockets, support for other HTTP methods (`PUT`, `DELETE`, etc...)
and configurable limits on HTTP usage.
- Full-block wired modems, allowing one to wrap non-solid peripherals (such as turtles, or chests if Plethora is
installed).
- Pocket computers can be held like maps, allowing you to view the screen without entering a GUI.
- Printed pages and books can be placed in item frames and held like maps.
- Several profiling and administration tools for server owners, via the `/computercraft` command. This allows operators
to track which computers are hogging resources, turn on and shutdown multiple computers at once and interact with
computers remotely.
- Closer emulation of standard Lua, adding the `debug` and `io` libraries. This also enables seeking within binary
files, meaning you don't need to read large files into memory.
- Allow running multiple computers on multiple threads, reducing latency on worlds with many computers.
## Relation to CCTweaks?
This mod has nothing to do with CCTweaks, though there is no denying the name is a throwback to it. That being said,
several features have been included, such as full block modems, the Cobalt runtime and map-like rendering for pocket
computers.
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It requires the [Minecraft Forge][forge] mod loader, but
[versions are available for Fabric][ccrestitched].
## Contributing
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started
@ -46,8 +17,7 @@ ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord g
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=computercraft), if that's
more your cup of tea.
I'd generally recommend you don't contact me directly (email, DM, etc...) unless absolutely necessary (i.e. in order to
report exploits). You'll get a far quicker response if you ask the whole community!
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
## Using
CC: Tweaked is hosted on my maven repo, and so is relatively simple to depend on. You may wish to add a soft (or hard)
@ -70,3 +40,9 @@ exposing more features.
We bundle the API sources with the jar, so documentation should be easily viewable within your editor. Alternatively,
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"

View File

@ -1,13 +1,12 @@
buildscript {
repositories {
mavenCentral()
maven {
name = "forge"
url = "https://maven.minecraftforge.net"
}
maven { url = "https://maven.minecraftforge.net" }
maven { url = 'https://maven.parchmentmc.org' }
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.19'
classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+'
classpath 'org.parchmentmc:librarian:1.+'
}
}
@ -23,6 +22,7 @@ plugins {
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.parchmentmc.librarian.forgegradle'
version = mod_version
@ -53,14 +53,7 @@ sourceSets {
srcDir 'src/generated/resources'
}
// Rename to testMod
cctest {
java {
compileClasspath += main.output
compileClasspath += configurations.compileClasspath
runtimeClasspath += main.output
}
}
testMod {}
}
minecraft {
@ -94,30 +87,46 @@ minecraft {
args '--mod', 'computercraft', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
}
testClient {
workingDirectory project.file('test-files/client')
parent runs.client
mods {
cctest {
source sourceSets.testMod
}
}
lazyToken('minecraft_classpath') {
(configurations.shade.copyRecursive().resolve() + configurations.testModExtra.copyRecursive().resolve())
.collect { it.absolutePath }
.join(File.pathSeparator)
}
}
testServer {
workingDirectory project.file('test-files/server')
parent runs.server
mods {
cctest {
source sourceSets.cctest
source sourceSets.testMod
}
}
lazyToken('minecraft_classpath') {
(configurations.shade.copyRecursive().resolve() + configurations.cctest.copyRecursive().resolve())
(configurations.shade.copyRecursive().resolve() + configurations.testModExtra.copyRecursive().resolve())
.collect { it.absolutePath }
.join(File.pathSeparator)
}
}
}
mappings channel: 'official', version: mc_version
mappings channel: 'parchment', version: "${mapping_version}-${mc_version}"
accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
accessTransformer file('src/test/resources/META-INF/accesstransformer.cfg')
accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg')
}
repositories {
mavenCentral()
maven {
@ -130,10 +139,11 @@ configurations {
shade
implementation.extendsFrom shade
cctest
cctestImplementation.extendsFrom cctest
cctJavadoc
testModExtra
testModImplementation.extendsFrom(testModExtra)
testModImplementation.extendsFrom(implementation)
}
dependencies {
@ -156,18 +166,15 @@ dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
cctest 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
testModImplementation sourceSets.main.output
testModExtra 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.1'
}
// Compile tasks
java {
modularity.inferModulePath = true
}
compileCctestJava {
compileTestModJava {
dependsOn(compileJava)
}
@ -210,7 +217,7 @@ jar {
jar.finalizedBy('reobfJar')
[compileJava, compileTestJava].forEach {
[compileJava, compileTestJava, compileTestModJava].forEach {
it.configure {
options.compilerArgs << "-Xlint" << "-Xlint:-processing"
}
@ -308,7 +315,9 @@ task illuaminateDocs(type: Exec, dependsOn: [minifyWeb, luaJavadoc]) {
}
task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
from 'doc/logo.png'
from 'doc'
include 'logo.png'
include 'images/**'
into "${project.docsDir}/lua"
}
@ -346,7 +355,7 @@ license {
}
}
[licenseTest, licenseFormatTest, licenseCctest, licenseFormatCctest].forEach {
[licenseTest, licenseFormatTest, licenseTestMod, licenseFormatTestMod].forEach {
it.configure {
include("**/*.java")
header file('config/license/main.txt')
@ -374,57 +383,60 @@ task setupServer(type: Copy) {
group "test server"
description "Sets up the environment for the test server."
from("src/cctest/server-files") {
from("src/testMod/server-files") {
include "eula.txt"
include "server.properties"
}
into "test-files/server"
}
tasks.register('testInGame', JavaExec.class).configure {
it.group('test server')
it.description("Runs tests on a temporary Minecraft server.")
it.dependsOn(setupServer, 'prepareRunTestServer', 'cleanTestInGame', 'compileCctestJava')
["Client", "Server"].forEach {name ->
tasks.register("test$name", JavaExec.class).configure {
it.group('In-game tests')
it.description("Runs tests on a temporary Minecraft instance.")
it.dependsOn(setupServer, "prepareRunTest$name", "cleanTest$name", 'compileTestModJava')
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
// isn't created until the task is configured (which is no good for us).
JavaExec exec = tasks.getByName('runTestServer')
exec.copyTo(it)
it.setClasspath(exec.getClasspath())
it.mainClass = exec.mainClass
it.setArgs(exec.getArgs())
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
// isn't created until the task is configured (which is no good for us).
JavaExec exec = tasks.getByName("runTest$name")
exec.copyTo(it)
it.setClasspath(exec.getClasspath())
it.mainClass = exec.mainClass
it.setArgs(exec.getArgs())
it.systemProperty('forge.logging.console.level', 'info')
it.systemProperty('cctest.run', 'true')
it.systemProperty('forge.logging.console.level', 'info')
it.systemProperty('cctest.run', 'true')
// Jacoco and modlauncher don't play well together as the classes loaded in-game don't
// match up with those written to disk. We get Jacoco to dump all classes to disk, and
// use that when generating the report.
def coverageOut = new File(buildDir, 'jacocoClassDump/testInGame')
jacoco.applyTo(it)
it.jacoco.setIncludes(["dan200.computercraft.*"])
it.jacoco.setClassDumpDir(coverageOut)
it.outputs.dir(coverageOut)
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.
it.jacoco.setIncludeNoLocationClasses(true)
}
tasks.register('jacocoTestInGameReport', JacocoReport.class).configure {
it.group('test server')
it.description('Generate coverage reports for in-game tests (testInGame)')
it.dependsOn('testInGame')
it.executionData(new File(buildDir, 'jacoco/testInGame.exec'))
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
it.classDirectories.from(new File(buildDir, 'jacocoClassDump/testInGame'))
it.reports {
xml.enabled true
html.enabled true
// Jacoco and modlauncher don't play well together as the classes loaded in-game don't
// match up with those written to disk. We get Jacoco to dump all classes to disk, and
// use that when generating the report.
def coverageOut = new File(buildDir, "jacocoClassDump/test$name")
jacoco.applyTo(it)
it.jacoco.setIncludes(["dan200.computercraft.*"])
it.jacoco.setClassDumpDir(coverageOut)
it.outputs.dir(coverageOut)
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.
it.jacoco.setIncludeNoLocationClasses(true)
}
tasks.register("jacocoTest${name}Report", JacocoReport.class).configure {
it.group('In-game')
it.description("Generate coverage reports for test$name")
it.dependsOn("test$name")
it.executionData(new File(buildDir, "jacoco/test${name}.exec"))
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
it.classDirectories.from(new File(buildDir, "jacocoClassDump/$name"))
it.reports {
xml.enabled true
html.enabled true
}
}
check.dependsOn("jacocoTest${name}Report")
}
check.dependsOn('jacocoTestInGameReport')
// Upload tasks
@ -478,10 +490,6 @@ curseforge {
releaseType = isStable ? 'release' : 'alpha'
changelog = "Release notes can be found on the GitHub repository (https://github.com/SquidDev-CC/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
relations {
incompatible "computercraft"
}
addGameVersion "${mc_version}"
}
}

View File

@ -50,6 +50,6 @@ exclude: |
(?x)^(
src/generated|
src/test/resources/test-rom/data/json-parsing/|
src/cctest/server-files/|
src/testMod/server-files/|
config/idea/
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
doc/images/peripherals.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
doc/images/turtle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -1,11 +1,55 @@
# ![CC: Tweaked](logo.png) [![Download CC: Tweaked on CurseForge](https://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
# ![CC: Tweaked](logo.png)
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
much-beloved [ComputerCraft], it continues its legacy with better performance, stability, and a wealth of new features.
CC: Tweaked is a fork of [ComputerCraft], adding programmable computers, turtles and more to Minecraft.
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It requires the [Minecraft Forge][forge] mod loader, but
[versions are available for Fabric][ccrestitched].
This website contains documentation for all Lua libraries and APIs from the latest version of CC: Tweaked. This
documentation is still in development, so will most likely be incomplete. If you've found something you think is wrong,
or would like to help out [please get in touch on GitHub][gh].
## Features
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
writing code and automating your Minecraft world.
![A ComputerCraft terminal open and ready to be programmed.](images/basic-terminal.png){.big-image}
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
solution here. They can move about the world, placing and breaking blocks, swinging a sword to protect you from zombies,
or whatever else you program them to!
![A turtle tunneling in Minecraft.](images/turtle.png){.big-image}
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
computers together with modems, and much more.
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
management systems.
![A chest's contents being read by a computer and displayed on a monitor.](images/peripherals.png){.big-image}
## Getting Started
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
little daunting getting started. Thankfully, there's several fantastic tutorials out there:
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
various APIs and peripherals provided by the mod.
If you get stuck, do pop in to the [Minecraft Computer Mod Discord guild][discord] or ComputerCraft's
[IRC channel][irc].
## Get Involved
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
[github]: https://github.com/SquidDev-CC/CC-Tweaked/ "CC: Tweaked on GitHub"
[bug]: https://github.com/SquidDev-CC/CC-Tweaked/issues/new/choose
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
[gh]: https://github.com/SquidDev-CC/CC-Tweaked "CC:Tweaked on GitHub"
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
[lua]: https://www.lua.org/ "Lua's main website"
[discord]: https://discord.computercraft.cc "The Minecraft Computer Mods Discord"
[irc]: http://webchat.esper.net/?channels=computercraft "IRC webchat on EsperNet"

View File

@ -3,5 +3,6 @@ mod_version=1.98.1
# Minecraft properties (update mods.toml when changing)
mc_version=1.17.1
mapping_version=2021.08.15
forge_version=37.0.34
# NO SERIOUSLY, UPDATE mods.toml WHEN CHANGING

View File

@ -1,35 +0,0 @@
package dan200.computercraft.ingame.api
import net.minecraft.commands.arguments.blocks.BlockInput
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTestAssertException
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.gametest.framework.GameTestSequence
import net.minecraft.world.level.block.state.BlockState
/**
* Wait until a computer has finished running and check it is OK.
*/
fun GameTestSequence.thenComputerOk(id: Int): GameTestSequence =
thenWaitUntil {
val computer = ComputerState.get(id)
if (computer == null || !computer.isDone) throw GameTestAssertException("Computer #${id} has not finished yet.")
}.thenExecute {
ComputerState.get(id).check()
}
/**
* 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()
}
/**
* Set a block within the test structure.
*/
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)

View File

@ -1,69 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.gametest.framework.GameTestRegistry;
import net.minecraft.gametest.framework.TestFunction;
import net.minecraft.server.MinecraftServer;
import java.io.IOException;
import java.io.UncheckedIOException;
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
import static net.minecraft.commands.Commands.literal;
/**
* Helper commands for importing/exporting the computer directory.
*/
class CCTestCommand
{
public static void register( CommandDispatcher<CommandSourceStack> dispatcher )
{
dispatcher.register( choice( "cctest" )
.then( literal( "import" ).executes( context -> {
importFiles( context.getSource().getServer() );
return 0;
} ) )
.then( literal( "export" ).executes( context -> {
exportFiles( context.getSource().getServer() );
int total = 0;
for( TestFunction function : GameTestRegistry.getAllTestFunctions() )
{
total += dispatcher.execute( "test import " + function.getTestName(), context.getSource() );
total += dispatcher.execute( "test export " + function.getTestName(), context.getSource() );
}
return total;
} ) )
);
}
public static void importFiles( MinecraftServer server )
{
try
{
Copier.replicate( TestMod.sourceDir.resolve( "computers" ), server.getServerDirectory().toPath().resolve( "world/computercraft" ) );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
}
public static void exportFiles( MinecraftServer server )
{
try
{
Copier.replicate( server.getServerDirectory().toPath().resolve( "world/computercraft" ), TestMod.sourceDir.resolve( "computers" ) );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
}
}

View File

@ -1,77 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
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 net.minecraft.gametest.framework.GameTestSequence;
/**
* 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, int) To check tests on the computer have passed.
*/
public class TestAPI extends ComputerState implements ILuaAPI
{
private final int id;
TestAPI( IComputerSystem system )
{
id = system.getID();
}
@Override
public void startup()
{
ComputerCraft.log.info( "Computer #{} has turned on.", id );
done = false;
error = null;
lookup.put( id, this );
}
@Override
public void shutdown()
{
ComputerCraft.log.info( "Computer #{} has shut down.", id );
if( lookup.get( id ) == this ) lookup.remove( id );
}
@Override
public String[] getNames()
{
return new String[] { "test" };
}
@LuaFunction
public final void fail( String message ) throws LuaException
{
ComputerCraft.log.error( "Computer #{} failed with {}", id, message );
if( done ) throw new LuaException( "Cannot call fail/ok multiple times." );
done = true;
error = message;
throw new LuaException( message );
}
@LuaFunction
public final void ok() throws LuaException
{
if( done ) throw new LuaException( "Cannot call fail/ok multiple times." );
done = true;
}
@LuaFunction
public final void log( String message )
{
ComputerCraft.log.info( "[Computer #{}] {}", id, message );
}
}

View File

@ -1,124 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
import dan200.computercraft.api.ComputerCraftAPI;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.gametest.framework.*;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks;
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
@Mod( TestMod.MOD_ID )
public class TestMod
{
public static final Path sourceDir = Paths.get( "../../src/cctest/server-files/" ).toAbsolutePath();
public static final String MOD_ID = "cctest";
public static final Logger log = LogManager.getLogger( MOD_ID );
private MultipleTestTracker runningTests = null;
private int countdown = 60;
public TestMod()
{
log.info( "CC: Test initialised" );
ComputerCraftAPI.registerAPIFactory( TestAPI::new );
TestLoader.setup();
StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString();
MinecraftForge.EVENT_BUS.addListener( ( RegisterCommandsEvent event ) -> {
log.info( "Starting server, registering command helpers." );
TestCommand.register( event.getDispatcher() );
CCTestCommand.register( event.getDispatcher() );
} );
MinecraftForge.EVENT_BUS.addListener( ( FMLServerStartedEvent event ) -> {
MinecraftServer server = event.getServer();
GameRules rules = server.getGameRules();
rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server );
rules.getRule( GameRules.RULE_WEATHER_CYCLE ).set( false, server );
rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server );
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 );
} );
MinecraftForge.EVENT_BUS.addListener( ( TickEvent.ServerTickEvent event ) -> {
if( event.phase != TickEvent.Phase.START ) return;
// Let the world settle a bit before starting tests.
countdown--;
if( countdown == 0 && System.getProperty( "cctest.run", "false" ).equals( "true" ) ) startTests();
GameTestTicker.SINGLETON.tick();
if( runningTests != null && runningTests.isDone() ) finishTests();
} );
}
private void startTests()
{
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
CommandSourceStack source = server.createCommandSourceStack();
Collection<TestFunction> tests = GameTestRegistry.getAllTestFunctions();
log.info( "Running {} tests...", tests.size() );
runningTests = new MultipleTestTracker( GameTestRunner.runTests(
tests, getStart( source ), Rotation.NONE, source.getLevel(), GameTestTicker.SINGLETON, 8
) );
}
private void finishTests()
{
log.info( "Finished tests - {} were run", runningTests.getTotalCount() );
if( runningTests.hasFailedRequired() )
{
log.error( "{} required tests failed", runningTests.getFailedRequiredCount() );
}
if( runningTests.hasFailedOptional() )
{
log.warn( "{} optional tests failed", runningTests.getFailedOptionalCount() );
}
if( ServerLifecycleHooks.getCurrentServer().isDedicatedServer() )
{
log.info( "Stopping server." );
// We can't exit in the main thread, as Minecraft registers a shutdown hook which results
// in a deadlock. So we do this weird janky thing!
Thread thread = new Thread( () -> System.exit( runningTests.hasFailedRequired() ? 1 : 0 ) );
thread.setDaemon( true );
thread.start();
}
}
private BlockPos getStart( CommandSourceStack source )
{
BlockPos pos = new BlockPos( source.getPosition() );
return new BlockPos( pos.getX(), source.getLevel().getHeightmapPos( Heightmap.Types.WORLD_SURFACE, pos ).getY(), pos.getZ() + 3 );
}
}

View File

@ -1,10 +0,0 @@
public-f net.minecraft.gametest.framework.TestFunction f_128067_
public-f net.minecraft.gametest.framework.TestFunction f_128071_
public-f net.minecraft.gametest.framework.TestFunction f_177798_
public-f net.minecraft.gametest.framework.TestFunction f_128072_
public-f net.minecraft.gametest.framework.TestFunction f_128070_
public-f net.minecraft.gametest.framework.TestFunction f_177799_
public-f net.minecraft.gametest.framework.TestFunction f_128074_
public-f net.minecraft.gametest.framework.TestFunction f_128073_
public-f net.minecraft.gametest.framework.TestFunction f_128069_
public-f net.minecraft.gametest.framework.TestFunction f_128068_

View File

@ -1,14 +0,0 @@
-- CraftOsTest.`Sends basic rednet messages`
rednet.open("top")
local id, msg
repeat
rednet.send(14, "Test msg") -- Keep sending, as other computer may not have started yet.
id, msg = rednet.receive(nil, 1)
print(id, msg)
until id == 14
test.eq("Test msg", msg)
test.ok()

View File

@ -1,4 +0,0 @@
-- DiskDriveTest.`Ejects disk`
disk.eject("right")
test.ok()

View File

@ -1,3 +0,0 @@
{
"computer": 14
}

View File

@ -1,526 +0,0 @@
{
DataVersion: 2730,
size: [5, 5, 5],
entities: [],
data: [
{
pos: [0, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [1, 1, 2],
state: "computercraft:computer_advanced{facing:north,state:on}",
nbt: {
Label: "Echo",
id: "computercraft:computer_advanced",
ComputerId: 14,
On: 1b
}
},
{
pos: [3, 1, 2],
state: "computercraft:computer_advanced{facing:north,state:blinking}",
nbt: {
Label: "Main",
id: "computercraft:computer_advanced",
ComputerId: 13,
On: 1b
}
},
{
pos: [1, 2, 2],
state: "computercraft:wireless_modem_normal{waterlogged:false,facing:down,on:true}",
nbt: {
id: "computercraft:wireless_modem_normal"
}
},
{
pos: [3, 2, 2],
state: "computercraft:wireless_modem_normal{waterlogged:false,facing:down,on:true}",
nbt: {
id: "computercraft:wireless_modem_normal"
}
},
{
pos: [0, 1, 0],
state: "minecraft:air"
},
{
pos: [1, 1, 0],
state: "minecraft:air"
},
{
pos: [2, 1, 0],
state: "minecraft:air"
},
{
pos: [3, 1, 0],
state: "minecraft:air"
},
{
pos: [4, 1, 0],
state: "minecraft:air"
},
{
pos: [0, 2, 0],
state: "minecraft:air"
},
{
pos: [1, 2, 0],
state: "minecraft:air"
},
{
pos: [2, 2, 0],
state: "minecraft:air"
},
{
pos: [3, 2, 0],
state: "minecraft:air"
},
{
pos: [4, 2, 0],
state: "minecraft:air"
},
{
pos: [0, 3, 0],
state: "minecraft:air"
},
{
pos: [1, 3, 0],
state: "minecraft:air"
},
{
pos: [2, 3, 0],
state: "minecraft:air"
},
{
pos: [3, 3, 0],
state: "minecraft:air"
},
{
pos: [4, 3, 0],
state: "minecraft:air"
},
{
pos: [0, 4, 0],
state: "minecraft:air"
},
{
pos: [1, 4, 0],
state: "minecraft:air"
},
{
pos: [2, 4, 0],
state: "minecraft:air"
},
{
pos: [3, 4, 0],
state: "minecraft:air"
},
{
pos: [4, 4, 0],
state: "minecraft:air"
},
{
pos: [0, 1, 1],
state: "minecraft:air"
},
{
pos: [1, 1, 1],
state: "minecraft:air"
},
{
pos: [2, 1, 1],
state: "minecraft:air"
},
{
pos: [3, 1, 1],
state: "minecraft:air"
},
{
pos: [4, 1, 1],
state: "minecraft:air"
},
{
pos: [0, 2, 1],
state: "minecraft:air"
},
{
pos: [1, 2, 1],
state: "minecraft:air"
},
{
pos: [2, 2, 1],
state: "minecraft:air"
},
{
pos: [3, 2, 1],
state: "minecraft:air"
},
{
pos: [4, 2, 1],
state: "minecraft:air"
},
{
pos: [0, 3, 1],
state: "minecraft:air"
},
{
pos: [1, 3, 1],
state: "minecraft:air"
},
{
pos: [2, 3, 1],
state: "minecraft:air"
},
{
pos: [3, 3, 1],
state: "minecraft:air"
},
{
pos: [4, 3, 1],
state: "minecraft:air"
},
{
pos: [0, 4, 1],
state: "minecraft:air"
},
{
pos: [1, 4, 1],
state: "minecraft:air"
},
{
pos: [2, 4, 1],
state: "minecraft:air"
},
{
pos: [3, 4, 1],
state: "minecraft:air"
},
{
pos: [4, 4, 1],
state: "minecraft:air"
},
{
pos: [0, 1, 2],
state: "minecraft:air"
},
{
pos: [2, 1, 2],
state: "minecraft:air"
},
{
pos: [4, 1, 2],
state: "minecraft:air"
},
{
pos: [0, 2, 2],
state: "minecraft:air"
},
{
pos: [2, 2, 2],
state: "minecraft:air"
},
{
pos: [4, 2, 2],
state: "minecraft:air"
},
{
pos: [0, 3, 2],
state: "minecraft:air"
},
{
pos: [1, 3, 2],
state: "minecraft:air"
},
{
pos: [2, 3, 2],
state: "minecraft:air"
},
{
pos: [3, 3, 2],
state: "minecraft:air"
},
{
pos: [4, 3, 2],
state: "minecraft:air"
},
{
pos: [0, 4, 2],
state: "minecraft:air"
},
{
pos: [1, 4, 2],
state: "minecraft:air"
},
{
pos: [2, 4, 2],
state: "minecraft:air"
},
{
pos: [3, 4, 2],
state: "minecraft:air"
},
{
pos: [4, 4, 2],
state: "minecraft:air"
},
{
pos: [0, 1, 3],
state: "minecraft:air"
},
{
pos: [1, 1, 3],
state: "minecraft:air"
},
{
pos: [2, 1, 3],
state: "minecraft:air"
},
{
pos: [3, 1, 3],
state: "minecraft:air"
},
{
pos: [4, 1, 3],
state: "minecraft:air"
},
{
pos: [0, 2, 3],
state: "minecraft:air"
},
{
pos: [1, 2, 3],
state: "minecraft:air"
},
{
pos: [2, 2, 3],
state: "minecraft:air"
},
{
pos: [3, 2, 3],
state: "minecraft:air"
},
{
pos: [4, 2, 3],
state: "minecraft:air"
},
{
pos: [0, 3, 3],
state: "minecraft:air"
},
{
pos: [1, 3, 3],
state: "minecraft:air"
},
{
pos: [2, 3, 3],
state: "minecraft:air"
},
{
pos: [3, 3, 3],
state: "minecraft:air"
},
{
pos: [4, 3, 3],
state: "minecraft:air"
},
{
pos: [0, 4, 3],
state: "minecraft:air"
},
{
pos: [1, 4, 3],
state: "minecraft:air"
},
{
pos: [2, 4, 3],
state: "minecraft:air"
},
{
pos: [3, 4, 3],
state: "minecraft:air"
},
{
pos: [4, 4, 3],
state: "minecraft:air"
},
{
pos: [0, 1, 4],
state: "minecraft:air"
},
{
pos: [1, 1, 4],
state: "minecraft:air"
},
{
pos: [2, 1, 4],
state: "minecraft:air"
},
{
pos: [3, 1, 4],
state: "minecraft:air"
},
{
pos: [4, 1, 4],
state: "minecraft:air"
},
{
pos: [0, 2, 4],
state: "minecraft:air"
},
{
pos: [1, 2, 4],
state: "minecraft:air"
},
{
pos: [2, 2, 4],
state: "minecraft:air"
},
{
pos: [3, 2, 4],
state: "minecraft:air"
},
{
pos: [4, 2, 4],
state: "minecraft:air"
},
{
pos: [0, 3, 4],
state: "minecraft:air"
},
{
pos: [1, 3, 4],
state: "minecraft:air"
},
{
pos: [2, 3, 4],
state: "minecraft:air"
},
{
pos: [3, 3, 4],
state: "minecraft:air"
},
{
pos: [4, 3, 4],
state: "minecraft:air"
},
{
pos: [0, 4, 4],
state: "minecraft:air"
},
{
pos: [1, 4, 4],
state: "minecraft:air"
},
{
pos: [2, 4, 4],
state: "minecraft:air"
},
{
pos: [3, 4, 4],
state: "minecraft:air"
},
{
pos: [4, 4, 4],
state: "minecraft:air"
}
],
palette: ["minecraft:polished_andesite", "computercraft:computer_advanced{facing:north,state:on}", "computercraft:computer_advanced{facing:north,state:blinking}", "computercraft:wireless_modem_normal{waterlogged:false,facing:down,on:true}", "minecraft:air"]
}

View File

@ -1,508 +0,0 @@
{
DataVersion: 2730,
size: [5, 5, 5],
entities: [],
data: [
{
pos: [0, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 0],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 1],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 2],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 3],
state: "minecraft:polished_andesite"
},
{
pos: [0, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [1, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [2, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [3, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [4, 0, 4],
state: "minecraft:polished_andesite"
},
{
pos: [0, 1, 0],
state: "minecraft:air"
},
{
pos: [1, 1, 0],
state: "minecraft:air"
},
{
pos: [2, 1, 0],
state: "minecraft:air"
},
{
pos: [3, 1, 0],
state: "minecraft:air"
},
{
pos: [4, 1, 0],
state: "minecraft:air"
},
{
pos: [0, 2, 0],
state: "minecraft:air"
},
{
pos: [1, 2, 0],
state: "minecraft:air"
},
{
pos: [2, 2, 0],
state: "minecraft:air"
},
{
pos: [3, 2, 0],
state: "minecraft:air"
},
{
pos: [4, 2, 0],
state: "minecraft:air"
},
{
pos: [0, 3, 0],
state: "minecraft:air"
},
{
pos: [1, 3, 0],
state: "minecraft:air"
},
{
pos: [2, 3, 0],
state: "minecraft:air"
},
{
pos: [3, 3, 0],
state: "minecraft:air"
},
{
pos: [4, 3, 0],
state: "minecraft:air"
},
{
pos: [0, 4, 0],
state: "minecraft:air"
},
{
pos: [1, 4, 0],
state: "minecraft:air"
},
{
pos: [2, 4, 0],
state: "minecraft:air"
},
{
pos: [3, 4, 0],
state: "minecraft:air"
},
{
pos: [4, 4, 0],
state: "minecraft:air"
},
{
pos: [0, 1, 1],
state: "minecraft:air"
},
{
pos: [1, 1, 1],
state: "minecraft:air"
},
{
pos: [2, 1, 1],
state: "minecraft:air"
},
{
pos: [3, 1, 1],
state: "minecraft:air"
},
{
pos: [4, 1, 1],
state: "minecraft:air"
},
{
pos: [0, 2, 1],
state: "minecraft:air"
},
{
pos: [1, 2, 1],
state: "minecraft:air"
},
{
pos: [2, 2, 1],
state: "minecraft:air"
},
{
pos: [3, 2, 1],
state: "minecraft:air"
},
{
pos: [4, 2, 1],
state: "minecraft:air"
},
{
pos: [0, 3, 1],
state: "minecraft:air"
},
{
pos: [1, 3, 1],
state: "minecraft:air"
},
{
pos: [2, 3, 1],
state: "minecraft:air"
},
{
pos: [3, 3, 1],
state: "minecraft:air"
},
{
pos: [4, 3, 1],
state: "minecraft:air"
},
{
pos: [0, 4, 1],
state: "minecraft:air"
},
{
pos: [1, 4, 1],
state: "minecraft:air"
},
{
pos: [2, 4, 1],
state: "minecraft:air"
},
{
pos: [3, 4, 1],
state: "minecraft:air"
},
{
pos: [4, 4, 1],
state: "minecraft:air"
},
{
pos: [0, 1, 2],
state: "minecraft:air"
},
{
pos: [1, 1, 2],
state: "minecraft:air"
},
{
pos: [2, 1, 2],
state: "minecraft:air"
},
{
pos: [3, 1, 2],
state: "minecraft:air"
},
{
pos: [4, 1, 2],
state: "minecraft:air"
},
{
pos: [0, 2, 2],
state: "minecraft:air"
},
{
pos: [1, 2, 2],
state: "minecraft:air"
},
{
pos: [2, 2, 2],
state: "minecraft:air"
},
{
pos: [3, 2, 2],
state: "minecraft:air"
},
{
pos: [4, 2, 2],
state: "minecraft:air"
},
{
pos: [0, 3, 2],
state: "minecraft:air"
},
{
pos: [1, 3, 2],
state: "minecraft:air"
},
{
pos: [2, 3, 2],
state: "minecraft:air"
},
{
pos: [3, 3, 2],
state: "minecraft:air"
},
{
pos: [4, 3, 2],
state: "minecraft:air"
},
{
pos: [0, 4, 2],
state: "minecraft:air"
},
{
pos: [1, 4, 2],
state: "minecraft:air"
},
{
pos: [2, 4, 2],
state: "minecraft:air"
},
{
pos: [3, 4, 2],
state: "minecraft:air"
},
{
pos: [4, 4, 2],
state: "minecraft:air"
},
{
pos: [0, 1, 3],
state: "minecraft:air"
},
{
pos: [1, 1, 3],
state: "minecraft:air"
},
{
pos: [2, 1, 3],
state: "minecraft:air"
},
{
pos: [3, 1, 3],
state: "minecraft:air"
},
{
pos: [4, 1, 3],
state: "minecraft:air"
},
{
pos: [0, 2, 3],
state: "minecraft:air"
},
{
pos: [1, 2, 3],
state: "minecraft:air"
},
{
pos: [2, 2, 3],
state: "minecraft:air"
},
{
pos: [3, 2, 3],
state: "minecraft:air"
},
{
pos: [4, 2, 3],
state: "minecraft:air"
},
{
pos: [0, 3, 3],
state: "minecraft:air"
},
{
pos: [1, 3, 3],
state: "minecraft:air"
},
{
pos: [2, 3, 3],
state: "minecraft:air"
},
{
pos: [3, 3, 3],
state: "minecraft:air"
},
{
pos: [4, 3, 3],
state: "minecraft:air"
},
{
pos: [0, 4, 3],
state: "minecraft:air"
},
{
pos: [1, 4, 3],
state: "minecraft:air"
},
{
pos: [2, 4, 3],
state: "minecraft:air"
},
{
pos: [3, 4, 3],
state: "minecraft:air"
},
{
pos: [4, 4, 3],
state: "minecraft:air"
},
{
pos: [0, 1, 4],
state: "minecraft:air"
},
{
pos: [1, 1, 4],
state: "minecraft:air"
},
{
pos: [2, 1, 4],
state: "minecraft:air"
},
{
pos: [3, 1, 4],
state: "minecraft:air"
},
{
pos: [4, 1, 4],
state: "minecraft:air"
},
{
pos: [0, 2, 4],
state: "minecraft:air"
},
{
pos: [1, 2, 4],
state: "minecraft:air"
},
{
pos: [2, 2, 4],
state: "minecraft:air"
},
{
pos: [3, 2, 4],
state: "minecraft:air"
},
{
pos: [4, 2, 4],
state: "minecraft:air"
},
{
pos: [0, 3, 4],
state: "minecraft:air"
},
{
pos: [1, 3, 4],
state: "minecraft:air"
},
{
pos: [2, 3, 4],
state: "minecraft:air"
},
{
pos: [3, 3, 4],
state: "minecraft:air"
},
{
pos: [4, 3, 4],
state: "minecraft:air"
},
{
pos: [0, 4, 4],
state: "minecraft:air"
},
{
pos: [1, 4, 4],
state: "minecraft:air"
},
{
pos: [2, 4, 4],
state: "minecraft:air"
},
{
pos: [3, 4, 4],
state: "minecraft:air"
},
{
pos: [4, 4, 4],
state: "minecraft:air"
}
],
palette: ["minecraft:polished_andesite", "minecraft:air"]
}

View File

@ -60,6 +60,7 @@ public class SoundManager
setPosition( position );
this.volume = volume;
this.pitch = pitch;
attenuation = Attenuation.LINEAR;
}
void setPosition( Vec3 position )

View File

@ -30,15 +30,15 @@ import dan200.computercraft.core.computer.ComputerSide;
*
* @cc.usage Toggle the redstone signal above the computer every 0.5 seconds.
*
* <pre>
* <pre>{@code
* while true do
* redstone.setOutput("top", not redstone.getOutput("top"))
* sleep(0.5)
* end
* </pre>
* }</pre>
* @cc.usage Mimic a redstone comparator in [subtraction mode][comparator].
*
* <pre>
* <pre>{@code
* while true do
* local rear = rs.getAnalogueInput("back")
* local sides = math.max(rs.getAnalogueInput("left"), rs.getAnalogueInput("right"))
@ -46,7 +46,7 @@ import dan200.computercraft.core.computer.ComputerSide;
*
* os.pullEvent("redstone") -- Wait for a change to inputs.
* end
* </pre>
* }</pre>
*
* [comparator]: https://minecraft.gamepedia.com/Redstone_Comparator#Subtract_signal_strength "Redstone Comparator on
* the Minecraft wiki."
@ -205,9 +205,9 @@ public class RedstoneAPI implements ILuaAPI
* @param mask The mask to test.
* @return If the colours are on.
* @cc.usage Check if @{colors.white} and @{colors.black} are on above the computer.
* <pre>
* <pre>{@code
* print(redstone.testBundledInput("top", colors.combine(colors.white, colors.black)))
* </pre>
* }</pre>
* @see #getBundledInput
*/
@LuaFunction

View File

@ -91,9 +91,9 @@ public class CommandAPI implements ILuaAPI
* @cc.treturn number|nil The number of "affected" objects, or `nil` if the command failed. The definition of this
* varies from command to command.
* @cc.usage Set the block above the command computer to stone.
* <pre>
* <pre>{@code
* commands.exec("setblock ~ ~1 ~ minecraft:stone")
* </pre>
* }</pre>
*/
@LuaFunction( mainThread = true )
public final Object[] exec( String command )
@ -117,9 +117,9 @@ public class CommandAPI implements ILuaAPI
* @return The "task id". When this command has been executed, it will queue a `task_complete` event with a matching id.
* @throws LuaException (hidden) If the task cannot be created.
* @cc.usage Asynchronously sets the block above the computer to stone.
* <pre>
* <pre>{@code
* commands.execAsync("~ ~1 ~ minecraft:stone")
* </pre>
* }</pre>
* @cc.see parallel One may also use the parallel API to run multiple commands at once.
*/
@LuaFunction

View File

@ -90,7 +90,6 @@ public final class NetworkHandler
}
/**
* /**
* Register packet, and a thread-unsafe handler for it.
*
* @param <T> The type of the packet to send.

View File

@ -34,7 +34,7 @@ public final class WiredModemLocalPeripheral
private static final String NBT_PERIPHERAL_TYPE = "PeripheralType";
private static final String NBT_PERIPHERAL_ID = "PeripheralId";
private int id;
private int id = -1;
private String type;
private IPeripheral peripheral;

View File

@ -28,11 +28,11 @@ import javax.annotation.Nullable;
* @cc.module monitor
* @cc.usage Write "Hello, world!" to an adjacent monitor:
*
* <pre>
* <pre>{@code
* local monitor = peripheral.find("monitor")
* monitor.setCursorPos(1, 1)
* monitor.write("Hello, world!")
* </pre>
* }</pre>
*/
public class MonitorPeripheral extends TermMethods implements IPeripheral
{

View File

@ -176,7 +176,8 @@ public abstract class SpeakerPeripheral implements IPeripheral
Level world = getLevel();
Vec3 pos = getPosition();
float range = Mth.clamp( volume, 1.0f, 3.0f ) * 16;
float actualVolume = Mth.clamp( volume, 0.0f, 3.0f );
float range = actualVolume * 16;
context.issueMainThreadTask( () -> {
MinecraftServer server = world.getServer();
@ -186,13 +187,13 @@ public abstract class SpeakerPeripheral implements IPeripheral
{
server.getPlayerList().broadcast(
null, pos.x, pos.y, pos.z, range, world.dimension(),
new ClientboundCustomSoundPacket( name, SoundSource.RECORDS, pos, range, pitch )
new ClientboundCustomSoundPacket( name, SoundSource.RECORDS, pos, actualVolume, pitch )
);
}
else
{
NetworkHandler.sendToAllAround(
new SpeakerPlayClientMessage( getSource(), pos, name, range, pitch ),
new SpeakerPlayClientMessage( getSource(), pos, name, actualVolume, pitch ),
world, pos, range
);
}

View File

@ -25,13 +25,13 @@ import net.minecraftforge.items.wrapper.PlayerMainInvWrapper;
* This API is only available on pocket computers. As such, you may use its presence to determine what kind of computer
* you are using:
*
* <pre>
* <pre>{@code
* if pocket then
* print("On a pocket computer")
* else
* print("On something else")
* end
* </pre>
* }</pre>
*
* @cc.module pocket
*/

View File

@ -0,0 +1,131 @@
{
"itemGroup.computercraft": "ComputerCraft",
"block.computercraft.computer_normal": "コンピューター",
"block.computercraft.computer_advanced": "高度なコンピューター",
"block.computercraft.computer_command": "コマンドコンピューター",
"block.computercraft.disk_drive": "ディスクドライブ",
"block.computercraft.printer": "プリンター",
"block.computercraft.speaker": "スピーカー",
"block.computercraft.monitor_normal": "モニター",
"block.computercraft.monitor_advanced": "高度なモニター",
"block.computercraft.wireless_modem_advanced": "エンダーモデム",
"block.computercraft.wired_modem": "有線モデム",
"block.computercraft.cable": "ネットワークケーブル",
"block.computercraft.wired_modem_full": "有線モデム",
"block.computercraft.turtle_normal": "タートル",
"block.computercraft.turtle_normal.upgraded": "%sタートル",
"block.computercraft.turtle_normal.upgraded_twice": "%s%sタートル",
"block.computercraft.turtle_advanced": "高度なタートル",
"block.computercraft.turtle_advanced.upgraded": "高度な%sタートル",
"item.computercraft.disk": "フロッピーディスク",
"item.computercraft.treasure_disk": "フロッピーディスク",
"item.computercraft.printed_page": "印刷された紙",
"item.computercraft.printed_pages": "印刷された紙束",
"item.computercraft.printed_book": "印刷された本",
"item.computercraft.pocket_computer_normal": "ポケットコンピュータ",
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ",
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ",
"upgrade.minecraft.diamond_shovel.adjective": "掘削",
"upgrade.minecraft.diamond_pickaxe.adjective": "採掘",
"upgrade.minecraft.diamond_axe.adjective": "伐採",
"upgrade.minecraft.diamond_hoe.adjective": "農耕",
"upgrade.minecraft.crafting_table.adjective": "クラフト",
"upgrade.computercraft.wireless_modem_advanced.adjective": "エンダー",
"upgrade.computercraft.speaker.adjective": "騒音",
"chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました",
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
"commands.computercraft.help.synopsis": "特定のコマンドのヘルプを提供します",
"commands.computercraft.help.desc": "このヘルプメッセージを表示します",
"commands.computercraft.help.no_children": "%s にサブコマンドはありません",
"commands.computercraft.help.no_command": "%s というコマンドはありません",
"commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。",
"commands.computercraft.dump.action": "このコンピュータの詳細を表示します",
"commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します",
"commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。",
"commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました",
"commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.turn_on.done": "%s/%s コンピューターを起動しました",
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
"commands.computercraft.tp.action": "このコンピューターへテレポートします",
"commands.computercraft.tp.not_player": "非プレイヤー用のターミナルを開くことができません",
"commands.computercraft.tp.not_there": "世界でコンピュータを見つけることができませんでした",
"commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。",
"commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。",
"commands.computercraft.view.action": "このコンピュータを見ます",
"commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません",
"commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。",
"commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します",
"commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
"commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します",
"commands.computercraft.track.stop.action": "追跡を中止するためにクリックしてください",
"commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません",
"commands.computercraft.track.dump.synopsis": "最新の追跡結果をダンプしてください",
"commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。",
"commands.computercraft.track.dump.no_timings": "利用可能なタイミングはありません",
"commands.computercraft.reload.synopsis": "コンピュータークラフトのコンフィグファイルを再読み込みします",
"commands.computercraft.reload.done": "コンフィグを再読み込みしました",
"commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します",
"commands.computercraft.generic.no_position": "<no pos>",
"commands.computercraft.generic.position": "%s, %s, %s",
"commands.computercraft.generic.yes": "Y",
"commands.computercraft.generic.no": "N",
"commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください",
"commands.computercraft.generic.exception": "未処理の例外 (%s)",
"commands.computercraft.generic.additional_rows": "%d行を追加…",
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません",
"argument.computercraft.computer.many_matching": "'%s'に一致する複数のコンピューター (インスタンス %s)",
"argument.computercraft.tracking_field.no_field": "'%s'は未知のフィールドです",
"argument.computercraft.argument_expected": "引数が期待される",
"tracking_field.computercraft.tasks.name": "タスク",
"tracking_field.computercraft.total.name": "合計時間",
"tracking_field.computercraft.average.name": "平均時間",
"tracking_field.computercraft.max.name": "最大時間",
"tracking_field.computercraft.server_count.name": "サーバータスク数",
"tracking_field.computercraft.server_time.name": "サーバータスク時間",
"tracking_field.computercraft.fs.name": "ファイルシステム演算",
"tracking_field.computercraft.turtle.name": "タートル演算",
"tracking_field.computercraft.http.name": "HTTPリクエスト",
"tracking_field.computercraft.http_upload.name": "HTTPアップロード",
"tracking_field.computercraft.http_download.name": "HTTPダウンロード",
"tracking_field.computercraft.websocket_incoming.name": "Websocket 受信",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket 送信",
"tracking_field.computercraft.coroutines_dead.name": "コルーチン削除",
"gui.computercraft.tooltip.copy": "クリップボードにコピー",
"gui.computercraft.tooltip.computer_id": "コンピュータID: %s",
"gui.computercraft.tooltip.disk_id": "ディスクID: %s",
"gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする",
"gui.computercraft.tooltip.turn_on.key": "Ctrl+R 長押し",
"gui.computercraft.tooltip.turn_off.key": "Ctrl+S 長押し",
"gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する",
"gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し",
"gui.computercraft.upload.success": "アップロードは成功しました",
"gui.computercraft.upload.failed": "アップロードに失敗しました",
"gui.computercraft.upload.failed.out_of_space": "これらのファイルに必要なスペースがコンピュータ上にありません。",
"gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。",
"gui.computercraft.upload.failed.overwrite_dir": "同じ名前のディレクトリがすでにあるため、%s をアップロードできません。",
"computercraft.gui.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
"gui.computercraft.upload.overwrite": "ファイルは上書きされます",
"gui.computercraft.upload.overwrite_button": "上書き",
"block.computercraft.wireless_modem_normal": "無線モデム",
"block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル",
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ",
"upgrade.minecraft.diamond_sword.adjective": "攻撃",
"upgrade.computercraft.wireless_modem_normal.adjective": "無線",
"chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました",
"commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
"commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します",
"commands.computercraft.track.dump.computer": "コンピューター",
"commands.computercraft.reload.desc": "コンピュータークラフトのコンフィグファイルを再読み込みします",
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
"tracking_field.computercraft.peripheral.name": "実行呼び出し",
"tracking_field.computercraft.coroutines_created.name": "コルーチン作成",
"gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする",
"gui.computercraft.upload.success.msg": "%d個のファイルがアップロードされました。",
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。",
"gui.computercraft.upload.overwrite.detail": "アップロード時に次のファイルが上書きされます。継続しますか?%s"
}

View File

@ -16,10 +16,12 @@ The structure of this module is based on [A Prettier Printer][prettier].
@module cc.pretty
@usage Print a table to the terminal
local pretty = require "cc.pretty"
pretty.print(pretty.pretty({ 1, 2, 3 }))
@usage Build a custom document and display it
local pretty = require "cc.pretty"
pretty.print(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world")))
]]
@ -74,6 +76,7 @@ end
-- colour.
-- @treturn Doc The document with the provided text.
-- @usage Write some blue text.
--
-- local pretty = require "cc.pretty"
-- pretty.print(pretty.text("Hello!", colours.blue))
local function text(text, colour)
@ -455,6 +458,7 @@ end
-- `function: xxxxxxxx` (`false` by default).
-- @treturn Doc The object formatted as a document.
-- @usage Display a table on the screen
--
-- local pretty = require "cc.pretty"
-- pretty.print(pretty.pretty({ 1, 2, 3 }))
local function pretty(obj, options)

View File

@ -290,6 +290,7 @@ end
-- @treturn number The index of the created process.
-- @see os.run
-- @usage Run the "hello" program, and set its title to "Hello!"
--
-- local id = multishell.launch({}, "/rom/programs/fun/hello.lua")
-- multishell.setTitle(id, "Hello!")
function multishell.launch(tProgramEnv, sProgramPath, ...)

View File

@ -16,7 +16,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.seconds
abstract class NullApiEnvironment : IAPIEnvironment {
@ -52,7 +51,7 @@ class AsyncRunner : NullApiEnvironment() {
override fun queueEvent(event: String?, vararg args: Any?) {
ComputerCraft.log.debug("Queue event $event ${args.contentToString()}")
if (!eventStream.offer(arrayOf(event, *args))) {
if (eventStream.trySend(arrayOf(event, *args)).isFailure) {
throw IllegalStateException("Queue is full")
}
}
@ -107,7 +106,7 @@ class AsyncRunner : NullApiEnvironment() {
private val empty: Array<Any?> = arrayOf()
@OptIn(ExperimentalTime::class)
fun runTest(timeout: Duration = 5.seconds, fn: suspend AsyncRunner.() -> Unit) {
fun runTest(timeout: Duration = Duration.seconds(5), fn: suspend AsyncRunner.() -> Unit) {
runBlocking {
val runner = AsyncRunner()
try {

View File

@ -1,7 +1,6 @@
package dan200.computercraft.ingame
import dan200.computercraft.ingame.api.modifyBlock
import dan200.computercraft.ingame.api.sequence
import dan200.computercraft.ingame.api.*
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper

View File

@ -10,5 +10,5 @@ class CraftOs_Test {
* Sends a rednet message to another a computer and back again.
*/
@GameTest
fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk(13) }
fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk("main") }
}

View File

@ -14,13 +14,13 @@ class Disk_Drive_Test {
* @see [#688](https://github.com/SquidDev-CC/CC-Tweaked/issues/688)
*/
@GameTest
fun Audio_disk(helper: GameTestHelper) = helper.sequence { thenComputerOk(3) }
fun Audio_disk(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
@GameTest
fun Ejects_disk(helper: GameTestHelper) = helper.sequence {
val stackAt = BlockPos(2, 2, 2)
this
.thenComputerOk(4)
.thenComputerOk()
.thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
}
}

View File

@ -0,0 +1,38 @@
package dan200.computercraft.ingame
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
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
}
}

View File

@ -1,7 +1,7 @@
package dan200.computercraft.ingame
import dan200.computercraft.ingame.api.sequence
import dan200.computercraft.ingame.api.setBlock
import dan200.computercraft.ingame.api.*
import dan200.computercraft.shared.Capabilities
import dan200.computercraft.shared.Registry
import dan200.computercraft.shared.peripheral.monitor.TileMonitor
import net.minecraft.commands.arguments.blocks.BlockInput
@ -29,7 +29,8 @@ class Monitor_Test {
context.setBlock(pos, Blocks.AIR.defaultBlockState())
context.setBlock(pos, toSet)
thenIdle(2)
this
.thenIdle(2)
.thenExecute {
val tile = context.getBlockEntity(pos)
if (tile !is TileMonitor) {
@ -42,4 +43,25 @@ class Monitor_Test {
}
}
}
@GameTest(batch = "client:Monitor_Test.Looks_acceptable", timeoutTicks = 400)
fun Looks_acceptable(helper: GameTestHelper) = helper.sequence {
this
.thenExecute { helper.normaliseScene() }
.thenExecute {
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, 2)) as TileMonitor
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()
}
}

View File

@ -7,7 +7,7 @@ import net.minecraft.gametest.framework.GameTestHelper
class Turtle_Test {
@GameTest(timeoutTicks = TIMEOUT)
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk(1) }
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can sheer sheep (and drop items)
@ -15,7 +15,7 @@ class Turtle_Test {
* @see [#537](https://github.com/SquidDev-CC/CC-Tweaked/issues/537)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk(5) }
fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can place lava.
@ -23,7 +23,7 @@ class Turtle_Test {
* @see [#518](https://github.com/SquidDev-CC/CC-Tweaked/issues/518)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk(5) }
fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can place when waterlogged.
@ -31,7 +31,7 @@ class Turtle_Test {
* @see [#385](https://github.com/SquidDev-CC/CC-Tweaked/issues/385)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk(7) }
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can pick up lava
@ -39,7 +39,7 @@ class Turtle_Test {
* @see [#297](https://github.com/SquidDev-CC/CC-Tweaked/issues/297)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk(8) }
fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can hoe dirt.
@ -47,7 +47,7 @@ class Turtle_Test {
* @see [#258](https://github.com/SquidDev-CC/CC-Tweaked/issues/258)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk(9) }
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can place monitors
@ -55,14 +55,14 @@ class Turtle_Test {
* @see [#691](https://github.com/SquidDev-CC/CC-Tweaked/issues/691)
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk(10) }
fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can place into compostors. These are non-typical inventories, so
* worth testing.
*/
@GameTest(timeoutTicks = TIMEOUT)
fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk(11) }
fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
/**
* Checks turtles can be cleaned in cauldrons.
@ -70,7 +70,7 @@ class Turtle_Test {
* Currently not required as turtles can no longer right-click cauldrons.
*/
@GameTest(required = false)
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk(12) }
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
companion object {
const val TIMEOUT = 200

View File

@ -9,35 +9,40 @@ import dan200.computercraft.ingame.mod.TestAPI;
import net.minecraft.gametest.framework.GameTestAssertException;
import net.minecraft.gametest.framework.GameTestSequence;
import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Assertion state of a computer.
*
* @see TestAPI For the Lua interface for this.
* @see TestExtensionsKt#thenComputerOk(GameTestSequence, int)
* @see TestExtensionsKt#thenComputerOk(GameTestSequence, String, String)
*/
public class ComputerState
{
protected static final Map<Integer, ComputerState> lookup = new ConcurrentHashMap<>();
public static final String DONE = "DONE";
protected boolean done;
protected static final Map<String, ComputerState> lookup = new ConcurrentHashMap<>();
protected final Set<String> markers = new HashSet<>();
protected String error;
public boolean isDone()
public boolean isDone( @Nonnull String marker )
{
return done;
return markers.contains( marker );
}
public void check()
public void check( @Nonnull String marker )
{
if( !done ) throw new GameTestAssertException( "Not yet done" );
if( !markers.contains( marker ) ) throw new IllegalStateException( "Not yet at " + marker );
if( error != null ) throw new GameTestAssertException( error );
}
public static ComputerState get( int id )
public static ComputerState get( String label )
{
return lookup.get( id );
return lookup.get( label );
}
}

View File

@ -0,0 +1,159 @@
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.GameTestHelper
import net.minecraft.gametest.framework.GameTestSequence
import net.minecraft.world.entity.decoration.ArmorStand
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.levelgen.Heightmap
import java.nio.file.Files
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Supplier
import javax.imageio.ImageIO
/**
* 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<Unit>? = 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"
val counter = AtomicInteger()
return this
// Wait until all chunks have been rendered and we're idle for an extended period.
.thenExecute { counter.set(0) }
.thenWaitUntil {
if (Minecraft.getInstance().levelRenderer.hasRenderedAllChunks()) {
val idleFor = counter.getAndIncrement()
if (idleFor <= 20) throw GameTestAssertException("Only idle for $idleFor ticks")
} else {
counter.set(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 }
.thenIdle(5) // Some delay before/after to ensure the render thread has caught up.
.thenOnClient { screenshot("$fullName.png") }
.thenIdle(2)
.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())
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}")
}
if (ImageUtils.areSame(screenshot, original)) return@thenExecute
ImageUtils.writeDifference(screenshotsPath.resolve("$fullName.diff.png"), screenshot, original)
throw GameTestAssertException("Images are different.")
}
}
val GameTestHelper.testName: String get() = testInfo.testName
/**
* 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()
}
/**
* Set a block within the test structure.
*/
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)
/**
* "Normalise" the current world in preparation for screenshots.
*
* Basically removes any dirt and replaces it with concrete.
*/
fun GameTestHelper.normaliseScene() {
val y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, absolutePos(BlockPos.ZERO))
for (x in -100..100) {
for (z in -100..100) {
val pos = y.offset(x, -3, z)
val block = level.getBlockState(pos).block
if (block == Blocks.DIRT || block == Blocks.GRASS_BLOCK) {
level.setBlock(pos, Blocks.WHITE_CONCRETE.defaultBlockState(), 3)
}
}
}
}
/**
* Position the player at an armor stand.
*/
fun GameTestHelper.positionAtArmorStand() {
val entities = level.getEntities(null, bounds) { it.name.string == testName }
if (entities.size <= 0 || entities[0] !is ArmorStand) throw IllegalStateException("Cannot find armor stand")
val stand = entities[0] as ArmorStand
val player = level.randomPlayer ?: throw NullPointerException("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) {
Screenshot.grab(minecraft.gameDirectory, name, minecraft.mainRenderTarget) { TestMod.log.info(it.string) }
}
}

View File

@ -0,0 +1,176 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
import com.mojang.brigadier.CommandDispatcher;
import dan200.computercraft.ComputerCraft;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.gametest.framework.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
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 javax.annotation.Nonnull;
import java.io.IOException;
import java.io.UncheckedIOException;
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
import static net.minecraft.commands.Commands.literal;
/**
* Helper commands for importing/exporting the computer directory.
*/
class CCTestCommand
{
public static void register( CommandDispatcher<CommandSourceStack> dispatcher )
{
dispatcher.register( choice( "cctest" )
.then( literal( "import" ).executes( context -> {
importFiles( context.getSource().getServer() );
return 0;
} ) )
.then( literal( "export" ).executes( context -> {
exportFiles( context.getSource().getServer() );
for( TestFunction function : GameTestRegistry.getAllTestFunctions() )
{
TestCommand.exportTestStructure( context.getSource(), function.getStructureName() );
}
return 0;
} ) )
.then( literal( "regen-structures" ).executes( context -> {
for( TestFunction function : GameTestRegistry.getAllTestFunctions() )
{
dispatcher.execute( "test import " + function.getTestName(), context.getSource() );
TestCommand.exportTestStructure( context.getSource(), function.getStructureName() );
}
return 0;
} ) )
.then( literal( "runall" ).executes( context -> {
GameTestRegistry.forgetFailedTests();
MultipleTestTracker result = TestHooks.runTests();
result.addListener( new Callback( context.getSource(), result ) );
result.addFailureListener( x -> GameTestRegistry.rememberFailedTest( x.getTestFunction() ) );
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() );
if( pos == null ) return error( context.getSource(), "No nearby test" );
StructureBlockEntity structureBlock = (StructureBlockEntity) player.getLevel().getBlockEntity( pos );
TestFunction info = GameTestRegistry.getTestFunction( structureBlock.getStructurePath() );
// Kill the existing armor stand
player
.getLevel().getEntities( EntityType.ARMOR_STAND, x -> x.isAlive() && x.getName().getString().equals( info.getTestName() ) )
.forEach( Entity::kill );
// And create a new one
CompoundTag nbt = new CompoundTag();
nbt.putBoolean( "Marker", true );
nbt.putBoolean( "Invisible", true );
ArmorStand armorStand = EntityType.ARMOR_STAND.create( player.getLevel() );
armorStand.readAdditionalSaveData( nbt );
armorStand.copyPosition( player );
armorStand.setCustomName( new TextComponent( info.getTestName() ) );
return 0;
} ) )
);
}
public static void importFiles( MinecraftServer server )
{
try
{
Copier.replicate( TestMod.sourceDir.resolve( "computers" ), server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ) );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
}
static void exportFiles( MinecraftServer server )
{
try
{
Copier.replicate( server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ), TestMod.sourceDir.resolve( "computers" ) );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
}
private static void promote()
{
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 );
}
}
private static class Callback implements GameTestListener
{
private final CommandSourceStack source;
private final MultipleTestTracker result;
Callback( CommandSourceStack source, MultipleTestTracker result )
{
this.source = source;
this.result = result;
}
@Override
public void testStructureLoaded( @Nonnull GameTestInfo tracker )
{
}
@Override
public void testFailed( @Nonnull GameTestInfo tracker )
{
TestHooks.writeResults( source, result );
}
@Override
public void testPassed( @Nonnull GameTestInfo tracker )
{
TestHooks.writeResults( source, result );
}
}
private static int error( CommandSourceStack source, String message )
{
source.sendFailure( new TextComponent( message ).withStyle( ChatFormatting.RED ) );
return 0;
}
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.ParticleStatus;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.tutorial.TutorialSteps;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.Difficulty;
import net.minecraft.world.level.DataPackConfig;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.StructureSettings;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Optional;
import static net.minecraft.world.level.levelgen.WorldGenSettings.withOverworld;
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT )
public final class ClientHooks
{
private static final Logger LOG = LogManager.getLogger( TestHooks.class );
private static boolean triggered = false;
private ClientHooks()
{
}
@SubscribeEvent
public static void onGuiInit( GuiScreenEvent.InitGuiEvent event )
{
if( triggered || !(event.getGui() instanceof TitleScreen) ) return;
triggered = true;
ClientHooks.openWorld();
}
private static void openWorld()
{
Minecraft minecraft = Minecraft.getInstance();
// Clear some options before we get any further.
minecraft.options.autoJump = false;
minecraft.options.renderClouds = CloudStatus.OFF;
minecraft.options.particles = ParticleStatus.MINIMAL;
minecraft.options.tutorialStep = TutorialSteps.NONE;
minecraft.options.renderDistance = 6;
minecraft.options.gamma = 1.0;
if( minecraft.getLevelSource().levelExists( "test" ) )
{
LOG.info( "World exists, loading it" );
Minecraft.getInstance().loadLevel( "test" );
}
else
{
LOG.info( "World does not exist, creating it for the first time" );
RegistryAccess.RegistryHolder registries = RegistryAccess.builtin();
Registry<DimensionType> dimensions = registries.registryOrThrow( Registry.DIMENSION_TYPE_REGISTRY );
Registry<Biome> biomes = registries.registryOrThrow( Registry.BIOME_REGISTRY );
FlatLevelGeneratorSettings flatSettings = FlatLevelGeneratorSettings.getDefault( biomes )
.withLayers(
Collections.singletonList( new FlatLayerInfo( 4, Blocks.WHITE_CONCRETE ) ),
new StructureSettings( Optional.empty(), Collections.emptyMap() )
);
flatSettings.setBiome( () -> biomes.get( Biomes.DESERT ) );
WorldGenSettings generator = new WorldGenSettings( 0, false, false, withOverworld(
dimensions,
DimensionType.defaultDimensions( dimensions, biomes, registries.registryOrThrow( Registry.NOISE_GENERATOR_SETTINGS_REGISTRY ), 0 ),
new FlatLevelSource( flatSettings )
) );
LevelSettings settings = new LevelSettings(
"test", GameType.CREATIVE, false, Difficulty.PEACEFUL, true,
new GameRules(), DataPackConfig.DEFAULT
);
Minecraft.getInstance().createLevel( "test", settings, registries, generator );
}
}
}

View File

@ -13,23 +13,25 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.Predicate;
final class Copier extends SimpleFileVisitor<Path>
{
private final Path sourceDir;
private final Path targetDir;
private final Predicate<Path> predicate;
private Copier( Path sourceDir, Path targetDir )
private Copier( Path sourceDir, Path targetDir, Predicate<Path> predicate )
{
this.sourceDir = sourceDir;
this.targetDir = targetDir;
this.predicate = predicate;
}
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attributes ) throws IOException
{
Path targetFile = targetDir.resolve( sourceDir.relativize( file ) );
Files.copy( file, targetFile );
if( predicate.test( file ) ) Files.copy( file, targetDir.resolve( sourceDir.relativize( file ) ) );
return FileVisitResult.CONTINUE;
}
@ -43,12 +45,17 @@ final class Copier extends SimpleFileVisitor<Path>
public static void copy( Path from, Path to ) throws IOException
{
Files.walkFileTree( from, new Copier( from, to ) );
Files.walkFileTree( from, new Copier( from, to, p -> true ) );
}
public static void replicate( Path from, Path to ) throws IOException
{
replicate( from, to, p -> true );
}
public static void replicate( Path from, Path to, Predicate<Path> check ) throws IOException
{
if( Files.exists( to ) ) MoreFiles.deleteRecursively( to );
copy( from, to );
Files.walkFileTree( from, new Copier( from, to, check ) );
}
}

View File

@ -0,0 +1,82 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.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.08% of pixels to fail. This allows for slight differences at the edges.
*/
private static final double PIXEL_THRESHOLD = 0.0008;
/**
* 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) );
}
}

View File

@ -0,0 +1,92 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
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 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.
*/
public class TestAPI extends ComputerState implements ILuaAPI
{
private final IComputerSystem system;
private String label;
TestAPI( IComputerSystem system )
{
this.system = system;
}
@Override
public void startup()
{
if( label == null ) label = system.getLabel();
if( label == null )
{
label = "#" + system.getID();
ComputerCraft.log.warn( "Computer {} has no label", label );
}
ComputerCraft.log.info( "Computer '{}' has turned on.", label );
markers.clear();
error = null;
lookup.put( label, this );
}
@Override
public void shutdown()
{
ComputerCraft.log.info( "Computer '{}' has shut down.", label );
if( lookup.get( label ) == this ) lookup.remove( label );
}
@Override
public String[] getNames()
{
return new String[] { "test" };
}
@LuaFunction
public final void fail( String message ) throws LuaException
{
ComputerCraft.log.error( "Computer '{}' failed with {}", label, message );
if( markers.contains( ComputerState.DONE ) ) throw new LuaException( "Cannot call fail/ok multiple times." );
markers.add( ComputerState.DONE );
error = message;
throw new LuaException( message );
}
@LuaFunction
public final void ok( Optional<String> marker ) throws LuaException
{
String actualMarker = marker.orElse( ComputerState.DONE );
if( markers.contains( ComputerState.DONE ) || markers.contains( actualMarker ) )
{
throw new LuaException( "Cannot call fail/ok multiple times." );
}
markers.add( actualMarker );
}
@LuaFunction
public final void log( String message )
{
ComputerCraft.log.info( "[Computer '{}'] {}", label, message );
}
}

View File

@ -0,0 +1,181 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.ingame.mod;
import net.minecraft.ChatFormatting;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.gametest.framework.*;
import net.minecraft.network.chat.TextComponent;
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.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks;
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.stream.Collectors;
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID )
public class TestHooks
{
private static final Logger LOG = LogManager.getLogger( TestHooks.class );
private static MultipleTestTracker runningTests = null;
private static boolean shutdown = false;
private static int countdown = 20;
@SubscribeEvent
public static void onRegisterCommands( RegisterCommandsEvent event )
{
LOG.info( "Starting server, registering command helpers." );
TestCommand.register( event.getDispatcher() );
CCTestCommand.register( event.getDispatcher() );
}
@SubscribeEvent
public static void onServerStarted( FMLServerStartedEvent event )
{
MinecraftServer server = event.getServer();
GameRules rules = server.getGameRules();
rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server );
rules.getRule( GameRules.RULE_WEATHER_CYCLE ).set( false, server );
rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server );
ServerLevel world = event.getServer().getLevel( Level.OVERWORLD );
if( world != null ) world.setDayTime( 6000 );
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 );
}
@SubscribeEvent
public static void onServerTick( TickEvent.ServerTickEvent event )
{
if( event.phase != TickEvent.Phase.START ) return;
// Let the world settle a bit before starting tests.
countdown--;
if( countdown == 0 && System.getProperty( "cctest.run", "false" ).equals( "true" ) ) startTests();
if( !SharedConstants.IS_RUNNING_IN_IDE ) GameTestTicker.SINGLETON.tick();
if( runningTests != null && runningTests.isDone() ) finishTests();
}
public static MultipleTestTracker runTests()
{
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
CommandSourceStack source = server.createCommandSourceStack();
Collection<TestFunction> tests = GameTestRegistry.getAllTestFunctions()
.stream()
.filter( x -> FMLLoader.getDist().isClient() | !x.getBatchName().startsWith( "client" ) )
.collect( Collectors.toList() );
LOG.info( "Running {} tests...", tests.size() );
Collection<GameTestBatch> batches = GameTestRunner.groupTestsIntoBatches( tests );
return new MultipleTestTracker( GameTestRunner.runTestBatches(
batches, getStart( source ), Rotation.NONE, source.getLevel(), GameTestTicker.SINGLETON, 8
) );
}
public static void writeResults( CommandSourceStack source, MultipleTestTracker result )
{
if( !result.isDone() ) return;
say( source, "Finished tests - " + result.getTotalCount() + " tests were run", ChatFormatting.WHITE );
if( result.hasFailedRequired() )
{
say( source, result.getFailedRequiredCount() + " required tests failed :(", ChatFormatting.RED );
}
else
{
say( source, "All required tests passed :)", ChatFormatting.GREEN );
}
if( result.hasFailedOptional() )
{
say( source, result.getFailedOptionalCount() + " optional tests failed", ChatFormatting.GRAY );
}
}
private static void startTests()
{
runningTests = runTests();
}
private static void finishTests()
{
if( shutdown ) return;
shutdown = true;
writeResults( ServerLifecycleHooks.getCurrentServer().createCommandSourceStack(), runningTests );
if( FMLLoader.getDist().isDedicatedServer() )
{
shutdownServer();
}
else
{
shutdownClient();
}
}
private static BlockPos getStart( CommandSourceStack source )
{
BlockPos pos = new BlockPos( source.getPosition() );
return new BlockPos( pos.getX(), source.getLevel().getHeightmapPos( Heightmap.Types.WORLD_SURFACE, pos ).getY(), pos.getZ() + 3 );
}
public static void shutdownCommon()
{
System.exit( runningTests.hasFailedRequired() ? 1 : 0 );
}
private static void shutdownServer()
{
// We can't exit normally as Minecraft registers a shutdown hook which results in a deadlock.
LOG.info( "Stopping server." );
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
new Thread( () -> {
server.halt( true );
shutdownCommon();
}, "Background shutdown" ).start();
}
private static void shutdownClient()
{
Minecraft minecraft = Minecraft.getInstance();
minecraft.execute( () -> {
LOG.info( "Stopping client." );
minecraft.level.disconnect();
minecraft.clearLevel();
minecraft.stop();
shutdownCommon();
} );
}
private static void say( CommandSourceStack source, String message, ChatFormatting colour )
{
source.sendFailure( new TextComponent( message ).withStyle( colour ) );
}
}

View File

@ -0,0 +1,34 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.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 );
TestLoader.setup();
StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString();
}
}

View File

@ -0,0 +1,6 @@
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

View File

@ -0,0 +1,11 @@
local label = os.getComputerLabel()
if label == nil then return test.fail("Label a computer to use it.") end
local fn, err = loadfile("tests/" .. label .. ".lua", nil, _ENV)
if not fn then return test.fail(err) end
local ok, err = pcall(fn)
if not ok then return test.fail(err) end
print("Run " .. label)
test.ok()

View File

@ -1,4 +1,5 @@
-- CraftOsTest.`Sends basic rednet messages`
os.getComputerID = function() return 1 end
os.computerID = os.getComputerID
rednet.open("top")
while true do

View File

@ -0,0 +1,10 @@
rednet.open("top")
local id, msg
repeat
rednet.send(1, "Test msg") -- Keep sending, as other computer may not have started yet.
id, msg = rednet.receive(nil, 1)
until id == 1
test.eq("Test msg", msg)

View File

@ -1,5 +1,2 @@
-- DiskDriveTest.`Audio disk`
test.eq(true, disk.hasAudio("right"), "Has audio")
test.eq("C418 - 13", disk.getAudioTitle("right"), "Audio title")
test.ok()

View File

@ -0,0 +1 @@
disk.eject("right")

View File

@ -0,0 +1,18 @@
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")

View File

@ -0,0 +1,12 @@
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")

View File

@ -0,0 +1,10 @@
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?!

View File

@ -0,0 +1,5 @@
local modem = peripheral.find("modem")
while true do
modem.transmit(12, 34, "Hello!")
sleep(1)
end

View File

@ -1,5 +1,3 @@
-- TurtleTest.`Cleaned with cauldrons`
local old_details = turtle.getItemDetail(1, true)
test.assert(turtle.place(), "Dyed turtle")
@ -7,6 +5,3 @@ 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")
test.ok()

View File

@ -1,5 +1,3 @@
-- TurtleTest.`Gather lava`
turtle.placeDown()
local item = turtle.getItemDetail()
@ -7,5 +5,3 @@ test.eq("minecraft:lava_bucket", item.name)
local has_down, down = turtle.inspectDown()
test.eq(false, has_down, "Air below")
test.ok()

View File

@ -1,9 +1,5 @@
-- Turtle.`Hoe dirt`
test.assert(turtle.dig())
local has_block, block = turtle.inspect()
test.assert(has_block, "Has block")
test.eq("minecraft:farmland", block.name)
test.ok()

View File

@ -1,9 +1,5 @@
-- TurtleTest.`Lava place`
test.assert(turtle.placeDown())
local ok, down = turtle.inspectDown()
test.assert(ok, "Has below")
test.eq("minecraft:lava", down.name, "Is lava")
test.ok()

View File

@ -1,10 +1,6 @@
-- Turtle.`Place Monitor`
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)
test.ok()

View File

@ -1,10 +1,6 @@
-- TurtleTest.`Place Waterlogged`
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)
test.ok()

View File

@ -1,7 +1,3 @@
-- TurtleTest.`Unequip refreshes peripheral`
test.eq("modem", peripheral.getType("right"), "Starts with a modem")
turtle.equipRight()
test.eq("drive", peripheral.getType("right"), "Unequipping gives a drive")
test.ok()

View File

@ -1,5 +1 @@
-- TurtleTest.`Use compostors`
test.eq(true, turtle.dropDown(), "Drop items into compostor")
test.ok()

View File

@ -0,0 +1,5 @@
{
"computer": 0,
"peripheral.monitor": 1,
"peripheral.printer": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -11,7 +11,7 @@ enforce-whitelist=false
force-gamemode=false
function-permission-level=2
gamemode=creative
generate-structures=true
generate-structures=false
generator-settings=
hardcore=false
level-name=world

View File

@ -39,8 +39,8 @@
{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:blinking}", nbt: {ComputerId: 2, On: 1b, 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, 2], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "computer_test.no_through_signal", On: 1b, id: "computercraft:computer_advanced"}},
{pos: [2, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:none,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"},
@ -132,10 +132,10 @@
palette: [
"minecraft:polished_andesite",
"minecraft:redstone_lamp{lit:false}",
"computercraft:computer_advanced{facing:north,state:blinking}",
"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:blinking}"
"minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:none}"
]
}

View File

@ -0,0 +1,139 @@
{
DataVersion: 2730,
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: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 0, Label: "craftos_test.sends_basic_rednet_messages.echo", On: 1b, id: "computercraft:computer_advanced"}},
{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: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "craftos_test.sends_basic_rednet_messages.main", On: 1b, id: "computercraft:computer_advanced"}},
{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: "computercraft:wireless_modem_normal{facing:down,on:true,waterlogged:false}", nbt: {id: "computercraft:wireless_modem_normal"}},
{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: "computercraft:wireless_modem_normal{facing:down,on:true,waterlogged:false}", nbt: {id: "computercraft:wireless_modem_normal"}},
{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:computer_advanced{facing:north,state:on}",
"computercraft:computer_advanced{facing:north,state:blinking}",
"computercraft:wireless_modem_normal{facing:down,on:true,waterlogged:false}"
]
}

View File

@ -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: 3, On: 1b, id: "computercraft:computer_advanced"}},
{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, 2], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
@ -33,8 +33,8 @@
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:disk_drive{facing:north,state:full}",
"computercraft:computer_advanced{facing:north,state:blinking}",
"minecraft:air"
"computercraft:computer_advanced{facing:north,state:blinking}"
]
}

View File

@ -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: 4, On: 1b, id: "computercraft:computer_advanced"}},
{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, 2], state: "minecraft:white_stained_glass"},
{pos: [3, 1, 3], state: "minecraft:white_stained_glass"},
{pos: [3, 1, 4], state: "minecraft:air"},
@ -132,8 +132,8 @@
palette: [
"minecraft:polished_andesite",
"minecraft:white_stained_glass",
"minecraft:air",
"computercraft:disk_drive{facing:south,state:full}",
"computercraft:computer_advanced{facing:north,state:blinking}",
"minecraft:air"
"computercraft:computer_advanced{facing:north,state:blinking}"
]
}

View File

@ -0,0 +1,145 @@
{
DataVersion: 2730,
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: "computercraft:printer{bottom:false,facing:north,top:false}", nbt: {Items: [], PageTitle: "", Printing: 0b, id: "computercraft:printer", term_bgColour: 15, term_cursorBlink: 0b, term_cursorX: 0, term_cursorY: 0, term_palette: [I; 1118481, 13388876, 5744206, 8349260, 3368652, 11691749, 5020082, 10066329, 5000268, 15905484, 8375321, 14605932, 10072818, 15040472, 15905331, 15790320], term_textBgColour_0: "fffffffffffffffffffffffff", term_textBgColour_1: "fffffffffffffffffffffffff", term_textBgColour_10: "fffffffffffffffffffffffff", term_textBgColour_11: "fffffffffffffffffffffffff", term_textBgColour_12: "fffffffffffffffffffffffff", term_textBgColour_13: "fffffffffffffffffffffffff", term_textBgColour_14: "fffffffffffffffffffffffff", term_textBgColour_15: "fffffffffffffffffffffffff", term_textBgColour_16: "fffffffffffffffffffffffff", term_textBgColour_17: "fffffffffffffffffffffffff", term_textBgColour_18: "fffffffffffffffffffffffff", term_textBgColour_19: "fffffffffffffffffffffffff", term_textBgColour_2: "fffffffffffffffffffffffff", term_textBgColour_20: "fffffffffffffffffffffffff", term_textBgColour_3: "fffffffffffffffffffffffff", term_textBgColour_4: "fffffffffffffffffffffffff", term_textBgColour_5: "fffffffffffffffffffffffff", term_textBgColour_6: "fffffffffffffffffffffffff", term_textBgColour_7: "fffffffffffffffffffffffff", term_textBgColour_8: "fffffffffffffffffffffffff", term_textBgColour_9: "fffffffffffffffffffffffff", term_textColour: 0, term_textColour_0: "0000000000000000000000000", term_textColour_1: "0000000000000000000000000", term_textColour_10: "0000000000000000000000000", term_textColour_11: "0000000000000000000000000", term_textColour_12: "0000000000000000000000000", term_textColour_13: "0000000000000000000000000", term_textColour_14: "0000000000000000000000000", term_textColour_15: "0000000000000000000000000", term_textColour_16: "0000000000000000000000000", term_textColour_17: "0000000000000000000000000", term_textColour_18: "0000000000000000000000000", term_textColour_19: "0000000000000000000000000", term_textColour_2: "0000000000000000000000000", term_textColour_20: "0000000000000000000000000", term_textColour_3: "0000000000000000000000000", term_textColour_4: "0000000000000000000000000", term_textColour_5: "0000000000000000000000000", term_textColour_6: "0000000000000000000000000", term_textColour_7: "0000000000000000000000000", term_textColour_8: "0000000000000000000000000", term_textColour_9: "0000000000000000000000000", term_text_0: " ", term_text_1: " ", term_text_10: " ", term_text_11: " ", term_text_12: " ", term_text_13: " ", term_text_14: " ", term_text_15: " ", term_text_16: " ", term_text_17: " ", term_text_18: " ", term_text_19: " ", term_text_2: " ", term_text_20: " ", term_text_3: " ", term_text_4: " ", term_text_5: " ", term_text_6: " ", term_text_7: " ", term_text_8: " ", term_text_9: " "}},
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:true,modem:north_on,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 1b, PeripheralId: 1, PeripheralType: "printer", id: "computercraft:cable"}},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:none}", nbt: {Height: 1, Width: 1, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [1, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [1, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 1b, PeripheralId: 1, PeripheralType: "monitor", id: "computercraft:cable"}},
{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: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{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, 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"},
{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:printer{bottom:false,facing:north,top:false}",
"computercraft:cable{cable:true,down:false,east:true,modem:north_on,north:true,south:false,up:false,waterlogged:false,west:false}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:none}",
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}",
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}",
"computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}",
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:false}",
"computercraft:computer_advanced{facing:north,state:blinking}",
"computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:true}"
]
}

View File

@ -0,0 +1,146 @@
{
DataVersion: 2730,
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: "computercraft:printer{bottom:false,facing:north,top:false}", nbt: {Items: [], PageTitle: "", Printing: 0b, id: "computercraft:printer", term_bgColour: 15, term_cursorBlink: 0b, term_cursorX: 0, term_cursorY: 0, term_palette: [I; 1118481, 13388876, 5744206, 8349260, 3368652, 11691749, 5020082, 10066329, 5000268, 15905484, 8375321, 14605932, 10072818, 15040472, 15905331, 15790320], term_textBgColour_0: "fffffffffffffffffffffffff", term_textBgColour_1: "fffffffffffffffffffffffff", term_textBgColour_10: "fffffffffffffffffffffffff", term_textBgColour_11: "fffffffffffffffffffffffff", term_textBgColour_12: "fffffffffffffffffffffffff", term_textBgColour_13: "fffffffffffffffffffffffff", term_textBgColour_14: "fffffffffffffffffffffffff", term_textBgColour_15: "fffffffffffffffffffffffff", term_textBgColour_16: "fffffffffffffffffffffffff", term_textBgColour_17: "fffffffffffffffffffffffff", term_textBgColour_18: "fffffffffffffffffffffffff", term_textBgColour_19: "fffffffffffffffffffffffff", term_textBgColour_2: "fffffffffffffffffffffffff", term_textBgColour_20: "fffffffffffffffffffffffff", term_textBgColour_3: "fffffffffffffffffffffffff", term_textBgColour_4: "fffffffffffffffffffffffff", term_textBgColour_5: "fffffffffffffffffffffffff", term_textBgColour_6: "fffffffffffffffffffffffff", term_textBgColour_7: "fffffffffffffffffffffffff", term_textBgColour_8: "fffffffffffffffffffffffff", term_textBgColour_9: "fffffffffffffffffffffffff", term_textColour: 0, term_textColour_0: "0000000000000000000000000", term_textColour_1: "0000000000000000000000000", term_textColour_10: "0000000000000000000000000", term_textColour_11: "0000000000000000000000000", term_textColour_12: "0000000000000000000000000", term_textColour_13: "0000000000000000000000000", term_textColour_14: "0000000000000000000000000", term_textColour_15: "0000000000000000000000000", term_textColour_16: "0000000000000000000000000", term_textColour_17: "0000000000000000000000000", term_textColour_18: "0000000000000000000000000", term_textColour_19: "0000000000000000000000000", term_textColour_2: "0000000000000000000000000", term_textColour_20: "0000000000000000000000000", term_textColour_3: "0000000000000000000000000", term_textColour_4: "0000000000000000000000000", term_textColour_5: "0000000000000000000000000", term_textColour_6: "0000000000000000000000000", term_textColour_7: "0000000000000000000000000", term_textColour_8: "0000000000000000000000000", term_textColour_9: "0000000000000000000000000", term_text_0: " ", term_text_1: " ", term_text_10: " ", term_text_11: " ", term_text_12: " ", term_text_13: " ", term_text_14: " ", term_text_15: " ", term_text_16: " ", term_text_17: " ", term_text_18: " ", term_text_19: " ", term_text_2: " ", term_text_20: " ", term_text_3: " ", term_text_4: " ", term_text_5: " ", term_text_6: " ", term_text_7: " ", term_text_8: " ", term_text_9: " "}},
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_on,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 1b, PeripheralId: 0, PeripheralType: "printer", id: "computercraft:cable"}},
{pos: [0, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:none}", nbt: {Height: 1, Width: 1, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [1, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 1b, PeripheralId: 0, PeripheralType: "monitor", id: "computercraft:cable"}},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{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: "computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{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: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, 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"},
{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:printer{bottom:false,facing:north,top:false}",
"computercraft:cable{cable:true,down:false,east:false,modem:north_on,north:true,south:true,up:false,waterlogged:false,west:false}",
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:true,south:false,up:false,waterlogged:false,west:false}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:none}",
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}",
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}",
"computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}",
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:true}",
"computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:false,up:false,waterlogged:false,west:true}",
"computercraft:computer_advanced{facing:north,state:blinking}"
]
}

View File

@ -0,0 +1,140 @@
{
DataVersion: 2730,
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: "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, 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"}},
{pos: [2, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
{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, 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:computer_advanced{facing:north,state:blinking}",
"computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:true,up:false,waterlogged:false,west:false}",
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}",
"computercraft:cable{cable:true,down:false,east:false,modem:west_off,north:true,south:false,up:false,waterlogged:false,west:true}"
]
}

View File

@ -0,0 +1,136 @@
{
DataVersion: 2730,
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"
]
}

View File

@ -0,0 +1,144 @@
{
DataVersion: 2730,
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: "computercraft:monitor_advanced{facing:north,orientation:north,state:lu}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 0, id: "computercraft:monitor_advanced"}},
{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:monitor_advanced{facing:north,orientation:north,state:lru}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 0, id: "computercraft:monitor_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: "computercraft:monitor_advanced{facing:north,orientation:north,state:ru}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
{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: "computercraft:monitor_advanced{facing:north,orientation:north,state:ld}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 1, id: "computercraft:monitor_advanced"}},
{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: "computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 1, id: "computercraft:monitor_advanced"}},
{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: "computercraft:monitor_advanced{facing:north,orientation:north,state:rd}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 1, id: "computercraft:monitor_advanced"}},
{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: [2, 1, 0], pos: [2.573085437203247d, 1.0d, 0.40392497295766816d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"monitor_test.looks_acceptable"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: 0s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 1b, PortalCooldown: 0, Pos: [121.57308543720325d, 6.0d, 139.40392497295767d], Pose: {}, Rotation: [-0.2999616f, 16.965813f], ShowArms: 0b, Small: 0b, UUID: [I; -1245769654, -1089124211, -1971323071, 221540869], id: "minecraft:armor_stand"}}
],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:monitor_advanced{facing:north,orientation:north,state:lu}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:lru}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:ru}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:ld}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}",
"computercraft:monitor_advanced{facing:north,orientation:north,state:rd}"
]
}

View File

@ -14,8 +14,8 @@
{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: 12, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 12, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "Clean turtle", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
{pos: [1, 1, 1], state: "minecraft:water_cauldron{level:3}"},
{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, 1], state: "minecraft:cauldron{level:3}"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
@ -33,8 +33,8 @@
entities: [],
palette: [
"minecraft:polished_andesite",
"computercraft:turtle_normal{facing:south,waterlogged:false}",
"minecraft:air",
"minecraft:water_cauldron{level:3}",
"computercraft:turtle_normal{facing:south,waterlogged:false}"
"minecraft:cauldron{level:3}"
]
}

View File

@ -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: 8, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:bucket"}], 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: 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, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},

View File

@ -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: 9, Fuel: 0, Items: [], 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: 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, 1], state: "minecraft:dirt"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},

View File

@ -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: 6, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:lava_bucket"}], 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: 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, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},

View File

@ -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: 10, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:monitor_advanced"}], 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: 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, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:dark_oak_planks"},
{pos: [2, 1, 0], state: "minecraft:air"},

View File

@ -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: 7, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:oak_fence"}], 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: 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, 2], state: "minecraft:water{level:0}"},
{pos: [2, 1, 3], state: "minecraft:white_stained_glass"},
{pos: [2, 1, 4], state: "minecraft:air"},

View File

@ -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: 5, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:shears", tag: {Damage: 0}}], 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: 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, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},

View File

@ -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: 1, Fuel: 0, Items: [], Label: "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: 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, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},

View File

@ -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: 11, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:spruce_sapling"}], Label: "Uses 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: 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, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},

View File

@ -1,3 +1,11 @@
/* Some misc styles */
.big-image {
display: block;
margin: 0 auto;
max-height: 400px;
}
/* Pretty tables, mostly inherited from table.definition-list */
table.pretty-table {
border-collapse: collapse;