mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-05 01:26:20 +00:00
Rewrite our gametest system
This is a noisier diff than I'd like as this is just a direct copy from
the multi-loader branch.
- Rename "ingame" package to "gametest"
- Don't chain GameTestSequence methods - it's actually much cleaner if
we just use Kotlin's implicit this syntax.
- Use our work in 71f81e1201
to write
computer tests using Kotlin instead of Lua. This means all the logic
is in one place, which is nice!
- Add a couple more tests for some of the more error-prone bits of
functionality.
This commit is contained in:
parent
b3702fed78
commit
c4184a33bc
@ -86,11 +86,17 @@ minecraft {
|
||||
val testMod = configurations["testModRuntimeClasspath"].resolve()
|
||||
val implementation = configurations.runtimeClasspath.get().resolve()
|
||||
val new = (testMod - implementation)
|
||||
.asSequence().filter { it.isFile }.map { it.absolutePath }
|
||||
.asSequence()
|
||||
.filter { it.isFile && !it.name.endsWith("-test-fixtures.jar") }
|
||||
.map { it.absolutePath }
|
||||
.joinToString(File.pathSeparator)
|
||||
if (old == null) new else old.get() + File.pathSeparator + new
|
||||
}
|
||||
|
||||
property("cctest.sources", file("src/testMod/resources/data/cctest").absolutePath)
|
||||
|
||||
arg("--mixin.config=computercraft-gametest.mixins.json")
|
||||
|
||||
mods.register("cctest") {
|
||||
source(sourceSets["testMod"])
|
||||
source(sourceSets["testFixtures"])
|
||||
@ -114,7 +120,6 @@ minecraft {
|
||||
mappings("parchment", "${libs.versions.parchmentMc.get()}-${libs.versions.parchment.get()}-$mcVersion")
|
||||
|
||||
accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg"))
|
||||
accessTransformer(file("src/testMod/resources/META-INF/accesstransformer.cfg"))
|
||||
}
|
||||
|
||||
mixin {
|
||||
|
@ -78,7 +78,9 @@
|
||||
|
||||
<!-- Javadoc -->
|
||||
<!-- TODO: Missing* checks for the dan200.computercraft.api package? -->
|
||||
<module name="AtclauseOrder" />
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
</module>
|
||||
<module name="InvalidJavadocPosition" />
|
||||
<module name="JavadocBlockTagLocation" />
|
||||
<module name="JavadocMethod"/>
|
||||
@ -107,7 +109,9 @@
|
||||
<module name="LocalFinalVariableName" />
|
||||
<module name="LocalVariableName" />
|
||||
<module name="MemberName" />
|
||||
<module name="MethodName" />
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||
</module>
|
||||
<module name="MethodTypeParameterName" />
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^dan200\.computercraft(\.[a-z][a-z0-9]*)*" />
|
||||
@ -115,11 +119,6 @@
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName">
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||
<property name="applyToPrivate" value="false" />
|
||||
</module>
|
||||
<module name="StaticVariableName">
|
||||
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||
<property name="applyToPrivate" value="true" />
|
||||
</module>
|
||||
<module name="TypeName" />
|
||||
|
||||
|
@ -5,13 +5,17 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.computer.core;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.ComputerCraftAPIImpl;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.core.ComputerContext;
|
||||
import dan200.computercraft.core.computer.ComputerThread;
|
||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThread;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
@ -41,6 +45,9 @@ public final class ServerContext
|
||||
{
|
||||
private static final LevelResource FOLDER = new LevelResource( ComputerCraft.MOD_ID );
|
||||
|
||||
@VisibleForTesting
|
||||
public static ILuaMachine.Factory luaMachine = CobaltLuaMachine::new;
|
||||
|
||||
private static @Nullable ServerContext instance;
|
||||
|
||||
private final MinecraftServer server;
|
||||
@ -57,7 +64,11 @@ public final class ServerContext
|
||||
this.server = server;
|
||||
storageDir = server.getWorldPath( FOLDER );
|
||||
mainThread = new MainThread();
|
||||
context = new ComputerContext( new Environment( server ), ComputerCraft.computerThreads, mainThread );
|
||||
context = new ComputerContext(
|
||||
new Environment( server ),
|
||||
new ComputerThread( ComputerCraft.computerThreads ),
|
||||
mainThread, luaMachine
|
||||
);
|
||||
idAssigner = new IDAssigner( storageDir.resolve( "ids.json" ) );
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,11 @@ import com.google.common.io.RecursiveDeleteOption;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.ingame.mod.TestMod;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
@ -19,10 +22,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.*;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ClientChatEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.io.File;
|
||||
@ -37,19 +36,24 @@ import java.util.Set;
|
||||
/**
|
||||
* Provides a {@literal /ccexport <path>} command which exports icons and recipes for all ComputerCraft items.
|
||||
*/
|
||||
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT )
|
||||
public class Exporter
|
||||
{
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientCommands( ClientChatEvent event )
|
||||
public static <S> void register( CommandDispatcher<S> dispatcher )
|
||||
{
|
||||
String prefix = "/ccexport";
|
||||
if( !event.getMessage().startsWith( prefix ) ) return;
|
||||
event.setCanceled( true );
|
||||
dispatcher.register(
|
||||
LiteralArgumentBuilder.<S>literal( "ccexport" )
|
||||
.then( RequiredArgumentBuilder.<S, String>argument( "path", StringArgumentType.string() )
|
||||
.executes( c -> {
|
||||
run( c.getArgument( "name", String.class ) );
|
||||
return 0;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
Path output = new File( event.getMessage().substring( prefix.length() ).trim() ).getAbsoluteFile().toPath();
|
||||
private static void run( String path )
|
||||
{
|
||||
Path output = new File( path ).getAbsoluteFile().toPath();
|
||||
if( !Files.isDirectory( output ) )
|
||||
{
|
||||
Minecraft.getInstance().gui.getChat().addMessage( new TextComponent( "Output path does not exist" ) );
|
||||
|
@ -3,9 +3,9 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.api;
|
||||
package dan200.computercraft.gametest.api;
|
||||
|
||||
import dan200.computercraft.ingame.mod.TestAPI;
|
||||
import dan200.computercraft.gametest.core.TestAPI;
|
||||
import net.minecraft.gametest.framework.GameTestAssertException;
|
||||
import net.minecraft.gametest.framework.GameTestSequence;
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest.api;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks classes containing {@linkplain GameTest game tests}.
|
||||
* <p>
|
||||
* This is used by Forge to automatically load and test classes.
|
||||
*/
|
||||
@Target( ElementType.TYPE )
|
||||
@Retention( RetentionPolicy.RUNTIME )
|
||||
public @interface GameTestHolder
|
||||
{
|
||||
}
|
@ -3,17 +3,16 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.mixin.gametest.TestCommandAccessor;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||
import net.minecraft.gametest.framework.StructureUtils;
|
||||
import net.minecraft.gametest.framework.TestCommand;
|
||||
import net.minecraft.gametest.framework.TestFunction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
@ -24,10 +23,10 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
@ -37,6 +36,8 @@ import static net.minecraft.commands.Commands.literal;
|
||||
*/
|
||||
class CCTestCommand
|
||||
{
|
||||
public static final LevelResource LOCATION = new LevelResource( ComputerCraft.MOD_ID );
|
||||
|
||||
public static void register( CommandDispatcher<CommandSourceStack> dispatcher )
|
||||
{
|
||||
dispatcher.register( choice( "cctest" )
|
||||
@ -49,7 +50,7 @@ class CCTestCommand
|
||||
|
||||
for( TestFunction function : GameTestRegistry.getAllTestFunctions() )
|
||||
{
|
||||
TestCommand.exportTestStructure( context.getSource(), function.getStructureName() );
|
||||
TestCommandAccessor.callExportTestStructure( context.getSource(), function.getStructureName() );
|
||||
}
|
||||
return 0;
|
||||
} ) )
|
||||
@ -57,17 +58,11 @@ class CCTestCommand
|
||||
for( TestFunction function : GameTestRegistry.getAllTestFunctions() )
|
||||
{
|
||||
dispatcher.execute( "test import " + function.getTestName(), context.getSource() );
|
||||
TestCommand.exportTestStructure( context.getSource(), function.getStructureName() );
|
||||
TestCommandAccessor.callExportTestStructure( context.getSource(), function.getStructureName() );
|
||||
}
|
||||
return 0;
|
||||
} ) )
|
||||
|
||||
.then( literal( "promote" ).executes( context -> {
|
||||
if( !FMLLoader.getDist().isClient() ) return error( context.getSource(), "Cannot run on server" );
|
||||
|
||||
promote();
|
||||
return 0;
|
||||
} ) )
|
||||
.then( literal( "marker" ).executes( context -> {
|
||||
ServerPlayer player = context.getSource().getPlayerOrException();
|
||||
BlockPos pos = StructureUtils.findNearestStructureBlock( player.blockPosition(), 15, player.getLevel() );
|
||||
@ -99,7 +94,7 @@ class CCTestCommand
|
||||
{
|
||||
try
|
||||
{
|
||||
Copier.replicate( TestMod.sourceDir.resolve( "computers" ), server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ) );
|
||||
Copier.replicate( getSourceComputerPath(), getWorldComputerPath( server ) );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
@ -111,7 +106,7 @@ class CCTestCommand
|
||||
{
|
||||
try
|
||||
{
|
||||
Copier.replicate( server.getWorldPath( new LevelResource( ComputerCraft.MOD_ID ) ), TestMod.sourceDir.resolve( "computers" ) );
|
||||
Copier.replicate( getWorldComputerPath( server ), getSourceComputerPath() );
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
@ -119,20 +114,14 @@ class CCTestCommand
|
||||
}
|
||||
}
|
||||
|
||||
private static void promote()
|
||||
private static Path getWorldComputerPath( MinecraftServer server )
|
||||
{
|
||||
try
|
||||
{
|
||||
Copier.replicate(
|
||||
Minecraft.getInstance().gameDirectory.toPath().resolve( "screenshots" ),
|
||||
TestMod.sourceDir.resolve( "screenshots" ),
|
||||
x -> !x.toFile().getName().endsWith( ".diff.png" )
|
||||
);
|
||||
}
|
||||
catch( IOException e )
|
||||
{
|
||||
throw new UncheckedIOException( e );
|
||||
}
|
||||
return server.getWorldPath( LOCATION ).resolve( "computer" ).resolve( "0" );
|
||||
}
|
||||
|
||||
private static Path getSourceComputerPath()
|
||||
{
|
||||
return TestHooks.sourceDir.resolve( "computer" );
|
||||
}
|
||||
|
||||
private static int error( CommandSourceStack source, String message )
|
@ -3,7 +3,7 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@ -36,7 +36,7 @@ import java.util.Optional;
|
||||
|
||||
import static net.minecraft.world.level.levelgen.WorldGenSettings.withOverworld;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT )
|
||||
@Mod.EventBusSubscriber( modid = "cctest", value = Dist.CLIENT )
|
||||
public final class ClientHooks
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger( TestHooks.class );
|
@ -3,7 +3,7 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import com.google.common.io.MoreFiles;
|
||||
import com.google.common.io.RecursiveDeleteOption;
|
@ -3,22 +3,22 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.IComputerSystem;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.ingame.api.ComputerState;
|
||||
import dan200.computercraft.ingame.api.TestExtensionsKt;
|
||||
import dan200.computercraft.gametest.api.ComputerState;
|
||||
import dan200.computercraft.gametest.api.TestExtensionsKt;
|
||||
import net.minecraft.gametest.framework.GameTestSequence;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* API exposed to computers to help write tests.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.gametest.api.Times;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTestRunner;
|
||||
import net.minecraft.gametest.framework.GameTestTicker;
|
||||
import net.minecraft.gametest.framework.StructureUtils;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class TestHooks
|
||||
{
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger( TestHooks.class );
|
||||
|
||||
public static final Path sourceDir = Paths.get( System.getProperty( "cctest.sources" ) ).normalize().toAbsolutePath();
|
||||
|
||||
public static void init()
|
||||
{
|
||||
ServerContext.luaMachine = ManagedComputers.INSTANCE;
|
||||
ComputerCraftAPI.registerAPIFactory( TestAPI::new );
|
||||
StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString();
|
||||
}
|
||||
|
||||
public static void onServerStarted( MinecraftServer server )
|
||||
{
|
||||
GameRules rules = server.getGameRules();
|
||||
rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server );
|
||||
|
||||
ServerLevel world = server.getLevel( Level.OVERWORLD );
|
||||
if( world != null ) world.setDayTime( Times.NOON );
|
||||
|
||||
LOGGER.info( "Cleaning up after last run" );
|
||||
GameTestRunner.clearAllTests( server.overworld(), new BlockPos( 0, -60, 0 ), GameTestTicker.SINGLETON, 200 );
|
||||
|
||||
// Delete server context and add one with a mutable machine factory. This allows us to set the factory for
|
||||
// specific test batches without having to reset all computers.
|
||||
for( var computer : ServerContext.get( server ).registry().getComputers() )
|
||||
{
|
||||
var label = computer.getLabel() == null ? "#" + computer.getID() : computer.getLabel();
|
||||
LOGGER.warn( "Unexpected computer {}", label );
|
||||
}
|
||||
|
||||
LOGGER.info( "Importing files" );
|
||||
CCTestCommand.importFiles( server );
|
||||
}
|
||||
}
|
144
src/testMod/java/dan200/computercraft/gametest/core/TestMod.java
Normal file
144
src/testMod/java/dan200/computercraft/gametest/core/TestMod.java
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest.core;
|
||||
|
||||
import dan200.computercraft.export.Exporter;
|
||||
import dan200.computercraft.gametest.api.GameTestHolder;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||
import net.minecraft.gametest.framework.StructureUtils;
|
||||
import net.minecraft.gametest.framework.TestFunction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.event.RegisterClientCommandsEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.RegisterGameTestsEvent;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.gametest.PrefixGameTestTemplate;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mod( "cctest" )
|
||||
public class TestMod
|
||||
{
|
||||
public TestMod()
|
||||
{
|
||||
TestHooks.init();
|
||||
|
||||
var bus = MinecraftForge.EVENT_BUS;
|
||||
bus.addListener( EventPriority.LOW, ( ServerStartedEvent e ) -> TestHooks.onServerStarted( e.getServer() ) );
|
||||
bus.addListener( ( RegisterCommandsEvent e ) -> CCTestCommand.register( e.getDispatcher() ) );
|
||||
bus.addListener( ( RegisterClientCommandsEvent e ) -> Exporter.register( e.getDispatcher() ) );
|
||||
|
||||
var modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
modBus.addListener( ( RegisterGameTestsEvent event ) -> {
|
||||
var holder = Type.getType( GameTestHolder.class );
|
||||
ModList.get().getAllScanData().stream()
|
||||
.map( ModFileScanData::getAnnotations )
|
||||
.flatMap( Collection::stream )
|
||||
.filter( a -> holder.equals( a.annotationType() ) )
|
||||
.forEach( x -> registerClass( x.clazz().getClassName(), event::register ) );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> loadClass( String name )
|
||||
{
|
||||
try
|
||||
{
|
||||
return Class.forName( name, true, TestMod.class.getClassLoader() );
|
||||
}
|
||||
catch( ReflectiveOperationException e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerClass( String className, Consumer<Method> fallback )
|
||||
{
|
||||
var klass = loadClass( className );
|
||||
for( var method : klass.getDeclaredMethods() )
|
||||
{
|
||||
var testInfo = method.getAnnotation( GameTest.class );
|
||||
if( testInfo == null )
|
||||
{
|
||||
fallback.accept( method );
|
||||
continue;
|
||||
}
|
||||
|
||||
GameTestRegistry.getAllTestFunctions().add( turnMethodIntoTestFunction( method, testInfo ) );
|
||||
GameTestRegistry.getAllTestClassNames().add( className );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom implementation of {@link GameTestRegistry#turnMethodIntoTestFunction(Method)} which makes
|
||||
* {@link GameTest#template()} behave the same as Fabric, namely in that it points to a {@link ResourceLocation},
|
||||
* rather than a test-class-specific structure.
|
||||
* <p>
|
||||
* This effectively acts as a global version of {@link PrefixGameTestTemplate}, just one which doesn't require Forge
|
||||
* to be present.
|
||||
*
|
||||
* @param method The method to register.
|
||||
* @param testInfo The test info.
|
||||
* @return The constructed test function.
|
||||
*/
|
||||
private static TestFunction turnMethodIntoTestFunction( Method method, GameTest testInfo )
|
||||
{
|
||||
var className = method.getDeclaringClass().getSimpleName().toLowerCase( Locale.ROOT );
|
||||
var testName = className + "." + method.getName().toLowerCase( Locale.ROOT );
|
||||
return new TestFunction(
|
||||
testInfo.batch(),
|
||||
testName,
|
||||
testInfo.template().isEmpty() ? testName : testInfo.template(),
|
||||
StructureUtils.getRotationForRotationSteps( testInfo.rotationSteps() ), testInfo.timeoutTicks(), testInfo.setupTicks(),
|
||||
testInfo.required(), testInfo.requiredSuccesses(), testInfo.attempts(),
|
||||
turnMethodIntoConsumer( method )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> Consumer<T> turnMethodIntoConsumer( Method method )
|
||||
{
|
||||
return value -> {
|
||||
try
|
||||
{
|
||||
Object instance = null;
|
||||
if( !Modifier.isStatic( method.getModifiers() ) )
|
||||
{
|
||||
instance = method.getDeclaringClass().getConstructor().newInstance();
|
||||
}
|
||||
|
||||
method.invoke( instance, value );
|
||||
}
|
||||
catch( InvocationTargetException e )
|
||||
{
|
||||
if( e.getCause() instanceof RuntimeException )
|
||||
{
|
||||
throw (RuntimeException) e.getCause();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException( e.getCause() );
|
||||
}
|
||||
}
|
||||
catch( ReflectiveOperationException e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.ingame.api.modifyBlock
|
||||
import dan200.computercraft.ingame.api.sequence
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.level.block.LeverBlock
|
||||
import net.minecraft.world.level.block.RedstoneLampBlock
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class Computer_Test {
|
||||
/**
|
||||
* Ensures redstone signals do not travel through computers.
|
||||
*
|
||||
* @see [#548](https://github.com/cc-tweaked/CC-Tweaked/issues/548)
|
||||
*/
|
||||
@GameTest
|
||||
fun No_through_signal(context: GameTestHelper) = context.sequence {
|
||||
val lamp = BlockPos(2, 2, 4)
|
||||
val lever = BlockPos(2, 2, 0)
|
||||
this
|
||||
.thenExecute {
|
||||
context.assertBlockState(lamp, { !it.getValue(RedstoneLampBlock.LIT) }, { "Lamp should not be lit" })
|
||||
context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) }
|
||||
}
|
||||
.thenIdle(3)
|
||||
.thenExecute {
|
||||
context.assertBlockState(
|
||||
lamp,
|
||||
{ !it.getValue(RedstoneLampBlock.LIT) },
|
||||
{ "Lamp should still not be lit" },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.ingame.api.sequence
|
||||
import dan200.computercraft.ingame.api.thenComputerOk
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class CraftOs_Test {
|
||||
/**
|
||||
* Sends a rednet message to another a computer and back again.
|
||||
*/
|
||||
@GameTest(timeoutTicks = 200)
|
||||
fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk("main") }
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.ingame.api.sequence
|
||||
import dan200.computercraft.ingame.api.thenComputerOk
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class Disk_Drive_Test {
|
||||
/**
|
||||
* Ensure audio disks exist and we can play them.
|
||||
*
|
||||
* @see [#688](https://github.com/cc-tweaked/CC-Tweaked/issues/688)
|
||||
*/
|
||||
@GameTest(timeoutTicks = Modem_Test.TIMEOUT)
|
||||
fun Audio_disk(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
@GameTest(timeoutTicks = Modem_Test.TIMEOUT)
|
||||
fun Ejects_disk(helper: GameTestHelper) = helper.sequence {
|
||||
val stackAt = BlockPos(2, 2, 2)
|
||||
this
|
||||
.thenComputerOk()
|
||||
.thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.ingame.api.sequence
|
||||
import dan200.computercraft.ingame.api.thenComputerOk
|
||||
import dan200.computercraft.shared.Registry
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class Modem_Test {
|
||||
@GameTest(timeoutTicks = TIMEOUT)
|
||||
fun Have_peripherals(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
@GameTest(timeoutTicks = TIMEOUT)
|
||||
fun Gains_peripherals(helper: GameTestHelper) = helper.sequence {
|
||||
val position = BlockPos(2, 2, 2)
|
||||
this
|
||||
.thenComputerOk(marker = "initial")
|
||||
.thenExecute {
|
||||
helper.setBlock(
|
||||
position,
|
||||
BlockCable.correctConnections(
|
||||
helper.level,
|
||||
helper.absolutePos(position),
|
||||
Registry.ModBlocks.CABLE.get().defaultBlockState().setValue(BlockCable.CABLE, true),
|
||||
),
|
||||
)
|
||||
}
|
||||
.thenComputerOk()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a modem message to another computer on the same network
|
||||
*/
|
||||
@GameTest(timeoutTicks = TIMEOUT)
|
||||
fun Transmits_messages(context: GameTestHelper) = context.sequence { thenComputerOk("receive") }
|
||||
|
||||
companion object {
|
||||
const val TIMEOUT = 200
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.ingame.api.*
|
||||
import dan200.computercraft.shared.Capabilities
|
||||
import dan200.computercraft.shared.Registry
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer
|
||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor
|
||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
import java.util.*
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class Monitor_Test {
|
||||
@GameTest
|
||||
fun Ensures_valid_on_place(context: GameTestHelper) = context.sequence {
|
||||
val pos = BlockPos(2, 2, 2)
|
||||
val tag = CompoundTag()
|
||||
tag.putInt("Width", 2)
|
||||
tag.putInt("Height", 2)
|
||||
|
||||
val toSet = BlockInput(
|
||||
Registry.ModBlocks.MONITOR_ADVANCED.get().defaultBlockState(),
|
||||
Collections.emptySet(),
|
||||
tag,
|
||||
)
|
||||
|
||||
context.setBlock(pos, Blocks.AIR.defaultBlockState())
|
||||
context.setBlock(pos, toSet)
|
||||
|
||||
this
|
||||
.thenIdle(2)
|
||||
.thenExecute {
|
||||
val tile = context.getBlockEntity(pos)
|
||||
if (tile !is TileMonitor) {
|
||||
context.fail("Expected tile to be monitor, is $tile", pos)
|
||||
return@thenExecute
|
||||
}
|
||||
|
||||
if (tile.width != 1 || tile.height != 1) {
|
||||
context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun looksAcceptable(helper: GameTestHelper, renderer: MonitorRenderer) = helper.sequence {
|
||||
this
|
||||
.thenExecute {
|
||||
ComputerCraft.monitorRenderer = renderer
|
||||
helper.positionAtArmorStand()
|
||||
|
||||
// Get the monitor and peripheral. This forces us to create a server monitor at this location.
|
||||
val monitor = helper.getBlockEntity(BlockPos(2, 2, 3), Registry.ModBlockEntities.MONITOR_ADVANCED.get())
|
||||
monitor.getCapability(Capabilities.CAPABILITY_PERIPHERAL)
|
||||
|
||||
val terminal = monitor.cachedServerMonitor!!.terminal!!
|
||||
terminal.write("Hello, world!")
|
||||
terminal.setCursorPos(1, 2)
|
||||
terminal.textColour = 2
|
||||
terminal.backgroundColour = 3
|
||||
terminal.write("Some coloured text")
|
||||
}
|
||||
.thenScreenshot()
|
||||
}
|
||||
|
||||
// @GameTest(batch = "Monitor_Test.Looks_acceptable", template = LOOKS_ACCEPTABLE)
|
||||
fun Looks_acceptable(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO)
|
||||
|
||||
// @GameTest(batch = "Monitor_Test.Looks_acceptable_dark", template = LOOKS_ACCEPTABLE_DARK)
|
||||
fun Looks_acceptable_dark(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO)
|
||||
|
||||
// @GameTest(batch = "Monitor_Test.Looks_acceptable_vbo", template = LOOKS_ACCEPTABLE)
|
||||
fun Looks_acceptable_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO)
|
||||
|
||||
// @GameTest(batch = "Monitor_Test.Looks_acceptable_dark_vbo", template = LOOKS_ACCEPTABLE_DARK)
|
||||
fun Looks_acceptable_dark_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO)
|
||||
|
||||
private companion object {
|
||||
const val LOOKS_ACCEPTABLE = "looks_acceptable"
|
||||
const val LOOKS_ACCEPTABLE_DARK = "looks_acceptable_dark"
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ingame.api.positionAtArmorStand
|
||||
import dan200.computercraft.ingame.api.sequence
|
||||
import dan200.computercraft.ingame.api.thenScreenshot
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
|
||||
class Printout_Test {
|
||||
// @GameTest(batch = "Printout_Test.In_frame_at_night", timeoutTicks = Timeouts.CLIENT_TIMEOUT)
|
||||
fun In_frame_at_night(helper: GameTestHelper) = helper.sequence {
|
||||
this
|
||||
.thenExecute { helper.positionAtArmorStand() }
|
||||
.thenScreenshot()
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
package dan200.computercraft.ingame
|
||||
|
||||
import dan200.computercraft.ComputerCraft
|
||||
import dan200.computercraft.api.detail.BasicItemDetailProvider
|
||||
import dan200.computercraft.api.detail.DetailRegistries
|
||||
import dan200.computercraft.ingame.api.*
|
||||
import dan200.computercraft.ingame.api.Timeouts.COMPUTER_TIMEOUT
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.gametest.GameTestHolder
|
||||
|
||||
@GameTestHolder(ComputerCraft.MOD_ID)
|
||||
class Turtle_Test {
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can sheer sheep (and drop items)
|
||||
*
|
||||
* @see [#537](https://github.com/cc-tweaked/CC-Tweaked/issues/537)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can place lava.
|
||||
*
|
||||
* @see [#518](https://github.com/cc-tweaked/CC-Tweaked/issues/518)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can place when waterlogged.
|
||||
*
|
||||
* @see [#385](https://github.com/cc-tweaked/CC-Tweaked/issues/385)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can pick up lava
|
||||
*
|
||||
* @see [#297](https://github.com/cc-tweaked/CC-Tweaked/issues/297)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can hoe dirt.
|
||||
*
|
||||
* @see [#258](https://github.com/cc-tweaked/CC-Tweaked/issues/258)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can place monitors
|
||||
*
|
||||
* @see [#691](https://github.com/cc-tweaked/CC-Tweaked/issues/691)
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can place into compostors. These are non-typical inventories, so
|
||||
* worth testing.
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can be cleaned in cauldrons.
|
||||
*
|
||||
* Currently not required as turtles can no longer right-click cauldrons.
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT, required = false)
|
||||
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||
|
||||
/**
|
||||
* Checks turtles can use IDetailProviders by getting details for a printed page.
|
||||
*/
|
||||
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||
fun Item_detail_provider(helper: GameTestHelper) = helper.sequence {
|
||||
this
|
||||
.thenComputerOk(marker = "initial")
|
||||
.thenExecute {
|
||||
// Register a dummy provider for printout items
|
||||
DetailRegistries.ITEM_STACK.addProvider(
|
||||
object : BasicItemDetailProvider<ItemPrintout>("printout", ItemPrintout::class.java) {
|
||||
override fun provideDetails(data: MutableMap<in String, Any>, stack: ItemStack, item: ItemPrintout) {
|
||||
data["type"] = item.type.toString()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.thenComputerOk()
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
package dan200.computercraft.ingame.api
|
||||
|
||||
import dan200.computercraft.ingame.mod.ImageUtils
|
||||
import dan200.computercraft.ingame.mod.TestMod
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.Screenshot
|
||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTestAssertException
|
||||
import net.minecraft.gametest.framework.GameTestAssertPosException
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.gametest.framework.GameTestSequence
|
||||
import net.minecraft.world.entity.decoration.ArmorStand
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import java.nio.file.Files
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.function.Supplier
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
object Times {
|
||||
const val NOON: Long = 6000
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom timeouts for various test types.
|
||||
*/
|
||||
object Timeouts {
|
||||
private const val SECOND: Int = 20
|
||||
|
||||
const val COMPUTER_TIMEOUT: Int = SECOND * 15
|
||||
|
||||
const val CLIENT_TIMEOUT: Int = SECOND * 20
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until a computer has finished running and check it is OK.
|
||||
*/
|
||||
fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = ComputerState.DONE): GameTestSequence {
|
||||
val label = parent.testName + (if (name == null) "" else ".$name")
|
||||
return this.thenWaitUntil {
|
||||
val computer = ComputerState.get(label)
|
||||
if (computer == null || !computer.isDone(marker)) throw GameTestAssertException("Computer '$label' has not reached $marker yet.")
|
||||
}.thenExecute {
|
||||
ComputerState.get(label).check(marker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task on the client
|
||||
*/
|
||||
fun GameTestSequence.thenOnClient(task: ClientTestHelper.() -> Unit): GameTestSequence {
|
||||
var future: CompletableFuture<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"
|
||||
|
||||
var counter = 0
|
||||
val hasScreenshot = AtomicBoolean()
|
||||
|
||||
return this
|
||||
// Wait until all chunks have been rendered and we're idle for an extended period.
|
||||
.thenExecute { counter = 0 }
|
||||
.thenWaitUntil {
|
||||
val renderer = Minecraft.getInstance().levelRenderer
|
||||
if (renderer.chunkRenderDispatcher != null && renderer.hasRenderedAllChunks()) {
|
||||
val idleFor = ++counter
|
||||
if (idleFor <= 20) throw GameTestAssertException("Only idle for $idleFor ticks")
|
||||
} else {
|
||||
counter = 0
|
||||
throw GameTestAssertException("Waiting for client to finish rendering")
|
||||
}
|
||||
}
|
||||
// Now disable the GUI, take a screenshot and reenable it. We sleep either side to give the client time to do
|
||||
// its thing.
|
||||
.thenExecute {
|
||||
Minecraft.getInstance().options.hideGui = true
|
||||
hasScreenshot.set(false)
|
||||
}
|
||||
.thenIdle(5) // Some delay before/after to ensure the render thread has caught up.
|
||||
.thenOnClient { screenshot("$fullName.png") { hasScreenshot.set(true) } }
|
||||
.thenWaitUntil { if (!hasScreenshot.get()) throw GameTestAssertException("Screenshot does not exist") }
|
||||
.thenExecute {
|
||||
Minecraft.getInstance().options.hideGui = false
|
||||
|
||||
val screenshotsPath = Minecraft.getInstance().gameDirectory.toPath().resolve("screenshots")
|
||||
val screenshotPath = screenshotsPath.resolve("$fullName.png")
|
||||
val originalPath = TestMod.sourceDir.resolve("screenshots").resolve("$fullName.png")
|
||||
|
||||
if (!Files.exists(originalPath)) throw GameTestAssertException("$fullName does not exist. Use `/cctest promote' to create it.")
|
||||
|
||||
val screenshot = ImageIO.read(screenshotPath.toFile())
|
||||
?: throw GameTestAssertException("Error reading screenshot from $screenshotPath")
|
||||
val original = ImageIO.read(originalPath.toFile())
|
||||
|
||||
if (screenshot.width != original.width || screenshot.height != original.height) {
|
||||
throw GameTestAssertException("$fullName screenshot is ${screenshot.width}x${screenshot.height} but original is ${original.width}x${original.height}")
|
||||
}
|
||||
|
||||
ImageUtils.writeDifference(screenshotsPath.resolve("$fullName.diff.png"), screenshot, original)
|
||||
if (!ImageUtils.areSame(screenshot, original)) throw GameTestAssertException("Images are different.")
|
||||
}
|
||||
}
|
||||
|
||||
val GameTestHelper.testName: String get() = testInfo.testName
|
||||
|
||||
val GameTestHelper.structureName: String get() = testInfo.structureName
|
||||
|
||||
/**
|
||||
* Modify a block state within the test.
|
||||
*/
|
||||
fun GameTestHelper.modifyBlock(pos: BlockPos, modify: (BlockState) -> BlockState) {
|
||||
setBlock(pos, modify(getBlockState(pos)))
|
||||
}
|
||||
|
||||
fun GameTestHelper.sequence(run: GameTestSequence.() -> GameTestSequence) {
|
||||
run(startSequence()).thenSucceed()
|
||||
}
|
||||
|
||||
fun <T : BlockEntity> GameTestHelper.getBlockEntity(pos: BlockPos, type: BlockEntityType<T>): T {
|
||||
val tile = getBlockEntity(pos)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return when {
|
||||
tile == null -> throw GameTestAssertPosException("Expected ${type.registryName}, but no tile was there", absolutePos(pos), pos, 0)
|
||||
tile.type != type -> throw GameTestAssertPosException("Expected ${type.registryName} but got ${tile.type.registryName}", absolutePos(pos), pos, 0)
|
||||
else -> tile as T
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block within the test structure.
|
||||
*/
|
||||
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)
|
||||
|
||||
/**
|
||||
* Position the player at an armor stand.
|
||||
*/
|
||||
fun GameTestHelper.positionAtArmorStand() {
|
||||
val entities = level.getEntities(null, bounds) { it.name.string == structureName }
|
||||
if (entities.size <= 0 || entities[0] !is ArmorStand) throw GameTestAssertException("Cannot find armor stand")
|
||||
|
||||
val stand = entities[0] as ArmorStand
|
||||
val player = level.randomPlayer ?: throw GameTestAssertException("Player does not exist")
|
||||
|
||||
player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot)
|
||||
}
|
||||
|
||||
class ClientTestHelper {
|
||||
val minecraft: Minecraft = Minecraft.getInstance()
|
||||
|
||||
fun screenshot(name: String, callback: () -> Unit = {}) {
|
||||
Screenshot.grab(
|
||||
minecraft.gameDirectory,
|
||||
name,
|
||||
minecraft.mainRenderTarget,
|
||||
) {
|
||||
TestMod.log.info(it.string)
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class ImageUtils
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger( ImageUtils.class );
|
||||
|
||||
/**
|
||||
* Allow 0.3% of pixels to fail. This allows for slight differences at the edges.
|
||||
*/
|
||||
private static final double PIXEL_THRESHOLD = 0.003;
|
||||
|
||||
/**
|
||||
* Maximum possible distance between two colours. Floating point differences means we need some fuzziness here.
|
||||
*/
|
||||
public static final int DISTANCE_THRESHOLD = 5;
|
||||
|
||||
private ImageUtils()
|
||||
{
|
||||
}
|
||||
|
||||
public static boolean areSame( BufferedImage left, BufferedImage right )
|
||||
{
|
||||
int width = left.getWidth(), height = left.getHeight();
|
||||
if( width != right.getWidth() || height != right.getHeight() ) return false;
|
||||
|
||||
int failed = 0, threshold = (int) (width * height * PIXEL_THRESHOLD);
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
int l = left.getRGB( x, y ), r = right.getRGB( x, y );
|
||||
if( (l & 0xFFFFFF) != (r & 0xFFFFFF) && distance( l, r, 0 ) + distance( l, r, 8 ) + distance( l, r, 16 ) >= DISTANCE_THRESHOLD )
|
||||
{
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( failed > 0 ) LOG.warn( "{} pixels failed comparing (threshold is {})", failed, threshold );
|
||||
return failed <= threshold;
|
||||
}
|
||||
|
||||
public static void writeDifference( Path path, BufferedImage left, BufferedImage right ) throws IOException
|
||||
{
|
||||
int width = left.getWidth(), height = left.getHeight();
|
||||
|
||||
BufferedImage copy = new BufferedImage( width, height, left.getType() );
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
{
|
||||
int l = left.getRGB( x, y ), r = right.getRGB( x, y );
|
||||
copy.setRGB( x, y, difference( l, r, 0 ) | difference( l, r, 8 ) | difference( l, r, 16 ) | 0xFF000000 );
|
||||
}
|
||||
}
|
||||
|
||||
ImageIO.write( copy, "png", path.toFile() );
|
||||
}
|
||||
|
||||
private static int difference( int l, int r, int shift )
|
||||
{
|
||||
return Math.abs( ((l >> shift) & 0xFF) - ((r >> shift) & 0xFF) ) << shift;
|
||||
}
|
||||
|
||||
private static int distance( int l, int r, int shift )
|
||||
{
|
||||
return Math.abs( ((l >> shift) & 0xFF) - ((r >> shift) & 0xFF) );
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
|
||||
import dan200.computercraft.ingame.api.Times;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID )
|
||||
public class TestHooks
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger( TestHooks.class );
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRegisterCommands( RegisterCommandsEvent event )
|
||||
{
|
||||
LOG.info( "Starting server, registering command helpers." );
|
||||
CCTestCommand.register( event.getDispatcher() );
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarted( ServerStartedEvent event )
|
||||
{
|
||||
MinecraftServer server = event.getServer();
|
||||
GameRules rules = server.getGameRules();
|
||||
rules.getRule( GameRules.RULE_DAYLIGHT ).set( false, server );
|
||||
|
||||
ServerLevel world = event.getServer().getLevel( Level.OVERWORLD );
|
||||
if( world != null ) world.setDayTime( Times.NOON );
|
||||
|
||||
// LOG.info( "Cleaning up after last run" );
|
||||
// CommandSourceStack source = server.createCommandSourceStack();
|
||||
// GameTestRunner.clearAllTests( source.getLevel(), getStart( source ), GameTestTicker.SINGLETON, 200 );
|
||||
|
||||
LOG.info( "Importing files" );
|
||||
CCTestCommand.importFiles( server );
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.ingame.mod;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import net.minecraft.gametest.framework.StructureUtils;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Mod( TestMod.MOD_ID )
|
||||
public class TestMod
|
||||
{
|
||||
public static final Path sourceDir = Paths.get( "../../src/testMod/server-files" ).normalize().toAbsolutePath();
|
||||
|
||||
public static final String MOD_ID = "cctest";
|
||||
|
||||
public static final Logger log = LogManager.getLogger( MOD_ID );
|
||||
|
||||
public TestMod()
|
||||
{
|
||||
log.info( "CC: Test initialised" );
|
||||
ComputerCraftAPI.registerAPIFactory( TestAPI::new );
|
||||
|
||||
StructureUtils.testStructuresDir = sourceDir.resolve( "structures" ).toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.gametest.framework.GameTestInfo;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin( GameTestHelper.class )
|
||||
public interface GameTestHelperAccessor
|
||||
{
|
||||
@Invoker
|
||||
AABB callGetBounds();
|
||||
|
||||
@Accessor
|
||||
GameTestInfo getTestInfo();
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestInfo;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin( GameTestInfo.class )
|
||||
public interface GameTestInfoAccessor
|
||||
{
|
||||
@Invoker( "getTick" )
|
||||
long computercraft$getTick();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestInfo;
|
||||
import net.minecraft.gametest.framework.GameTestSequence;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin( GameTestSequence.class )
|
||||
public interface GameTestSequenceAccessor
|
||||
{
|
||||
@Accessor
|
||||
GameTestInfo getParent();
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import dan200.computercraft.gametest.core.TestHooks;
|
||||
import net.minecraft.gametest.framework.GameTestAssertException;
|
||||
import net.minecraft.gametest.framework.GameTestInfo;
|
||||
import net.minecraft.gametest.framework.GameTestSequence;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin( GameTestSequence.class )
|
||||
class GameTestSequenceMixin
|
||||
{
|
||||
@Shadow
|
||||
@Final
|
||||
GameTestInfo parent;
|
||||
|
||||
/**
|
||||
* Override {@link GameTestSequence#tickAndContinue(long)} to catch non-{@link GameTestAssertException} failures.
|
||||
*
|
||||
* @param ticks The current tick.
|
||||
* @author Jonathan Coates
|
||||
* @reason There's no sense doing this in a more compatible way for game tests.
|
||||
*/
|
||||
@Overwrite
|
||||
public void tickAndContinue( long ticks )
|
||||
{
|
||||
try
|
||||
{
|
||||
tick( ticks );
|
||||
}
|
||||
catch( GameTestAssertException ignored )
|
||||
{
|
||||
// Mimic the original behaviour.
|
||||
}
|
||||
catch( AssertionError e )
|
||||
{
|
||||
parent.fail( e );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
// Fail the test, rather than crashing the server.
|
||||
TestHooks.LOGGER.error( "{} threw unexpected exception", parent.getTestName(), e );
|
||||
parent.fail( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Shadow
|
||||
private void tick( long tick )
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.gametest.framework.TestCommand;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin( TestCommand.class )
|
||||
public interface TestCommandAccessor
|
||||
{
|
||||
@Invoker
|
||||
static int callExportTestStructure( CommandSourceStack source, String structure )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.core.apis.RedstoneAPI
|
||||
import dan200.computercraft.core.computer.ComputerSide
|
||||
import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.LeverBlock
|
||||
import net.minecraft.world.level.block.RedstoneLampBlock
|
||||
|
||||
@GameTestHolder
|
||||
class Computer_Test {
|
||||
/**
|
||||
* Ensures redstone signals do not travel through computers.
|
||||
*
|
||||
* @see [#548](https://github.com/cc-tweaked/CC-Tweaked/issues/548)
|
||||
*/
|
||||
@GameTest
|
||||
fun No_through_signal(context: GameTestHelper) = context.sequence {
|
||||
val lamp = BlockPos(2, 2, 4)
|
||||
val lever = BlockPos(2, 2, 0)
|
||||
thenExecute {
|
||||
context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit")
|
||||
context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) }
|
||||
}
|
||||
thenIdle(3)
|
||||
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the above, but with a repeater before the computer
|
||||
*/
|
||||
@GameTest
|
||||
fun No_through_signal_reverse(context: GameTestHelper) = context.sequence {
|
||||
val lamp = BlockPos(2, 2, 4)
|
||||
val lever = BlockPos(2, 2, 0)
|
||||
thenExecute {
|
||||
context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit")
|
||||
context.modifyBlock(lever) { x -> x.setValue(LeverBlock.POWERED, true) }
|
||||
}
|
||||
thenIdle(3)
|
||||
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should still not be lit") }
|
||||
}
|
||||
|
||||
@GameTest
|
||||
fun Set_and_destroy(context: GameTestHelper) = context.sequence {
|
||||
val lamp = BlockPos(2, 2, 3)
|
||||
|
||||
thenOnComputer { getApi<RedstoneAPI>().setOutput(ComputerSide.BACK, true) }
|
||||
thenIdle(3)
|
||||
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, true, "Lamp should be lit") }
|
||||
thenExecute { context.setBlock(BlockPos(2, 2, 2), Blocks.AIR) }
|
||||
thenIdle(4)
|
||||
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") }
|
||||
}
|
||||
|
||||
// TODO: More redstone connectivity tests!
|
||||
// TODO: Computer peripherals (including command computers)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.GameTestHolder
|
||||
import dan200.computercraft.gametest.api.Timeouts
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.gametest.api.thenComputerOk
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
|
||||
@GameTestHolder
|
||||
class CraftOs_Test {
|
||||
/**
|
||||
* Sends a rednet message to another a computer and back again.
|
||||
*/
|
||||
@GameTest(timeoutTicks = Timeouts.COMPUTER_TIMEOUT)
|
||||
fun Sends_basic_rednet_messages(context: GameTestHelper) = context.sequence { thenComputerOk("main") }
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.GameTestHolder
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.gametest.api.thenOnComputer
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.item.Items
|
||||
|
||||
@GameTestHolder
|
||||
class Disk_Drive_Test {
|
||||
/**
|
||||
* Ensure audio disks exist and we can play them.
|
||||
*
|
||||
* @see [#688](https://github.com/cc-tweaked/CC-Tweaked/issues/688)
|
||||
*/
|
||||
@GameTest
|
||||
fun Audio_disk(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
callPeripheral("right", "hasAudio")
|
||||
.assertArrayEquals(true, message = "Disk has audio")
|
||||
|
||||
callPeripheral("right", "getAudioTitle")
|
||||
.assertArrayEquals("C418 - 13", message = "Correct audio title")
|
||||
}
|
||||
}
|
||||
|
||||
@GameTest
|
||||
fun Ejects_disk(helper: GameTestHelper) = helper.sequence {
|
||||
val stackAt = BlockPos(2, 2, 2)
|
||||
thenOnComputer { callPeripheral("right", "ejectDisk") }
|
||||
thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
|
||||
}
|
||||
|
||||
// TODO: Ejecting disk unmounts and closes files
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.GameTestHolder
|
||||
import dan200.computercraft.gametest.api.Structures
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.shared.Registry
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity
|
||||
import net.minecraft.world.level.storage.loot.BuiltInLootTables
|
||||
|
||||
@GameTestHolder
|
||||
class Loot_Test {
|
||||
/**
|
||||
* Test that the loot tables will spawn in treasure disks.
|
||||
*/
|
||||
@GameTest(template = Structures.DEFAULT)
|
||||
fun Chest_contains_disk(context: GameTestHelper) = context.sequence {
|
||||
thenExecute {
|
||||
val pos = BlockPos(2, 2, 2)
|
||||
|
||||
context.setBlock(pos, Blocks.CHEST)
|
||||
val chest = context.getBlockEntity(pos) as ChestBlockEntity
|
||||
chest.setLootTable(BuiltInLootTables.SIMPLE_DUNGEON, 123)
|
||||
chest.unpackLootTable(null)
|
||||
|
||||
context.assertContainerContains(pos, Registry.ModItems.TREASURE_DISK.get())
|
||||
}
|
||||
}
|
||||
}
|
103
src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt
Normal file
103
src/testMod/kotlin/dan200/computercraft/gametest/Modem_Test.kt
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
import dan200.computercraft.core.apis.PeripheralAPI
|
||||
import dan200.computercraft.core.computer.ComputerSide
|
||||
import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import kotlinx.coroutines.delay
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@GameTestHolder
|
||||
class Modem_Test {
|
||||
@GameTest
|
||||
fun Have_peripherals(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
assertEquals(listOf("monitor_0", "printer_0", "right"), getPeripheralNames(), "Starts with peripherals")
|
||||
}
|
||||
}
|
||||
|
||||
@GameTest
|
||||
fun Gains_peripherals(helper: GameTestHelper) = helper.sequence {
|
||||
val position = BlockPos(2, 2, 2)
|
||||
thenOnComputer {
|
||||
assertEquals(listOf("back"), getPeripheralNames(), "Starts with peripherals")
|
||||
}
|
||||
thenExecute {
|
||||
helper.setBlock(
|
||||
position,
|
||||
BlockCable.correctConnections(
|
||||
helper.level,
|
||||
helper.absolutePos(position),
|
||||
dan200.computercraft.shared.Registry.ModBlocks.CABLE.get().defaultBlockState().setValue(BlockCable.CABLE, true),
|
||||
),
|
||||
)
|
||||
}
|
||||
thenIdle(1)
|
||||
thenOnComputer {
|
||||
assertEquals(listOf("back", "monitor_1", "printer_1"), getPeripheralNames(), "Gains new peripherals")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a modem message to another computer on the same network
|
||||
*/
|
||||
@GameTest
|
||||
fun Transmits_messages(context: GameTestHelper) = context.sequence {
|
||||
thenStartComputer("send") {
|
||||
val modem = findPeripheral("modem") ?: throw IllegalStateException("Cannot find modem")
|
||||
while (true) {
|
||||
callPeripheral(modem, "transmit", 12, 34, "Hello")
|
||||
delay(50.milliseconds)
|
||||
}
|
||||
}
|
||||
thenOnComputer("receive") {
|
||||
val modem = findPeripheral("modem") ?: throw IllegalStateException("Cannot find modem")
|
||||
callPeripheral(modem, "open", 12)
|
||||
|
||||
pullEvent("modem_message")
|
||||
.assertArrayEquals("modem_message", "left", 12, 34, "Hello", 4, message = "Modem message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LuaTaskContext.findPeripheral(type: String): String? {
|
||||
val peripheral = getApi<PeripheralAPI>()
|
||||
for (side in ComputerSide.NAMES) {
|
||||
val hasType = peripheral.hasType(side, type)
|
||||
if (hasType != null && hasType[0] == true) return side
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private suspend fun LuaTaskContext.getPeripheralNames(): List<String> {
|
||||
val peripheral = getApi<PeripheralAPI>()
|
||||
val peripherals = mutableListOf<String>()
|
||||
for (side in ComputerSide.NAMES) {
|
||||
if (!peripheral.isPresent(side)) continue
|
||||
peripherals.add(side)
|
||||
|
||||
val hasType = peripheral.hasType(side, "modem")
|
||||
if (hasType == null || hasType[0] != true) continue
|
||||
|
||||
val names = peripheral.call(context, ObjectArguments(side, "getNamesRemote")).await() ?: continue
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
peripherals.addAll(names[0] as Collection<String>)
|
||||
}
|
||||
|
||||
peripherals.sort()
|
||||
return peripherals
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.GameTestHolder
|
||||
import dan200.computercraft.gametest.api.getBlockEntity
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.gametest.api.setBlock
|
||||
import dan200.computercraft.shared.Registry
|
||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import java.util.*
|
||||
|
||||
@GameTestHolder
|
||||
class Monitor_Test {
|
||||
@GameTest
|
||||
fun Ensures_valid_on_place(context: GameTestHelper) = context.sequence {
|
||||
val pos = BlockPos(2, 2, 2)
|
||||
|
||||
thenExecute {
|
||||
val tag = CompoundTag()
|
||||
tag.putInt("Width", 2)
|
||||
tag.putInt("Height", 2)
|
||||
|
||||
val toSet = BlockInput(
|
||||
Registry.ModBlocks.MONITOR_ADVANCED.get().defaultBlockState(),
|
||||
Collections.emptySet(),
|
||||
tag,
|
||||
)
|
||||
|
||||
context.setBlock(pos, Blocks.AIR.defaultBlockState())
|
||||
context.setBlock(pos, toSet)
|
||||
}
|
||||
thenIdle(2)
|
||||
thenExecute {
|
||||
val tile = context.getBlockEntity(pos, Registry.ModBlockEntities.MONITOR_ADVANCED.get())
|
||||
|
||||
if (tile.width != 1 || tile.height != 1) {
|
||||
context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.GameTestHolder
|
||||
import dan200.computercraft.gametest.api.Structures
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.shared.Registry
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestAssertException
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.CraftingRecipe
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import java.util.*
|
||||
|
||||
@GameTestHolder
|
||||
class Recipe_Test {
|
||||
/**
|
||||
* Test that crafting results contain NBT data.
|
||||
*
|
||||
* Mostly useful for Fabric, where we need a mixin for this.
|
||||
*/
|
||||
@GameTest(template = Structures.DEFAULT)
|
||||
fun Craft_result_has_nbt(context: GameTestHelper) = context.sequence {
|
||||
thenExecute {
|
||||
val container = CraftingContainer(DummyMenu, 3, 3)
|
||||
container.setItem(0, ItemStack(Items.SKELETON_SKULL))
|
||||
container.setItem(1, ItemStack(Registry.ModItems.COMPUTER_ADVANCED.get()))
|
||||
|
||||
val recipe: Optional<CraftingRecipe> = context.level.server.recipeManager
|
||||
.getRecipeFor(RecipeType.CRAFTING, container, context.level)
|
||||
if (!recipe.isPresent) throw GameTestAssertException("No recipe matches")
|
||||
|
||||
val result = recipe.get().assemble(container)
|
||||
|
||||
val owner = CompoundTag()
|
||||
owner.putString("Name", "dan200")
|
||||
owner.putString("Id", "f3c8d69b-0776-4512-8434-d1b2165909eb")
|
||||
val tag = CompoundTag()
|
||||
tag.put("SkullOwner", owner)
|
||||
|
||||
assertEquals(tag, result.tag, "Expected NBT tags to be the same")
|
||||
}
|
||||
}
|
||||
|
||||
object DummyMenu : AbstractContainerMenu(MenuType.GENERIC_9x1, 0) {
|
||||
override fun quickMoveStack(player: Player, slot: Int): ItemStack = ItemStack.EMPTY
|
||||
override fun stillValid(p0: Player): Boolean = true
|
||||
}
|
||||
}
|
252
src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt
Normal file
252
src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.api.detail.BasicItemDetailProvider
|
||||
import dan200.computercraft.api.detail.DetailRegistries
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
import dan200.computercraft.core.apis.PeripheralAPI
|
||||
import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.shared.Registry
|
||||
import dan200.computercraft.shared.media.items.ItemPrintout
|
||||
import dan200.computercraft.shared.peripheral.monitor.BlockMonitor
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState
|
||||
import dan200.computercraft.shared.turtle.apis.TurtleAPI
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.entity.item.PrimedTnt
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.FenceBlock
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.array
|
||||
import org.hamcrest.Matchers.instanceOf
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import java.util.*
|
||||
|
||||
@GameTestHolder
|
||||
class Turtle_Test {
|
||||
@GameTest
|
||||
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<PeripheralAPI>().getType("right").assertArrayEquals("modem", message = "Starts with a modem")
|
||||
getApi<TurtleAPI>().equipRight().await()
|
||||
getApi<PeripheralAPI>().getType("right").assertArrayEquals("drive", message = "Unequipping gives a drive")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can sheer sheep (and drop items)
|
||||
*
|
||||
* @see [#537](https://github.com/cc-tweaked/CC-Tweaked/issues/537)
|
||||
*/
|
||||
@GameTest
|
||||
fun Shears_sheep(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().placeDown(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Shears the sheep")
|
||||
|
||||
assertEquals("minecraft:white_wool", getTurtleItemDetail(2)["name"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can place lava.
|
||||
*
|
||||
* @see [#518](https://github.com/cc-tweaked/CC-Tweaked/issues/518)
|
||||
*/
|
||||
@GameTest
|
||||
fun Place_lava(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().placeDown(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Placed lava")
|
||||
}
|
||||
thenExecute { helper.assertBlockPresent(Blocks.LAVA, BlockPos(2, 2, 2)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can place when waterlogged.
|
||||
*
|
||||
* @see [#385](https://github.com/cc-tweaked/CC-Tweaked/issues/385)
|
||||
*/
|
||||
@GameTest
|
||||
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().place(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Placed oak fence")
|
||||
}
|
||||
thenExecute {
|
||||
helper.assertBlockIs(BlockPos(2, 2, 2), { it.block == Blocks.OAK_FENCE && it.getValue(FenceBlock.WATERLOGGED) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can pick up lava
|
||||
*
|
||||
* @see [#297](https://github.com/cc-tweaked/CC-Tweaked/issues/297)
|
||||
*/
|
||||
@GameTest
|
||||
fun Gather_lava(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().placeDown(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Picked up lava")
|
||||
|
||||
assertEquals("minecraft:lava_bucket", getTurtleItemDetail()["name"])
|
||||
}
|
||||
thenExecute { helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can hoe dirt.
|
||||
*
|
||||
* @see [#258](https://github.com/cc-tweaked/CC-Tweaked/issues/258)
|
||||
*/
|
||||
@GameTest
|
||||
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().dig(Optional.empty()).await()
|
||||
.assertArrayEquals(true, message = "Dug with hoe")
|
||||
}
|
||||
thenExecute { helper.assertBlockPresent(Blocks.FARMLAND, BlockPos(1, 2, 1)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can place monitors
|
||||
*
|
||||
* @see [#691](https://github.com/cc-tweaked/CC-Tweaked/issues/691)
|
||||
*/
|
||||
@GameTest
|
||||
fun Place_monitor(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().place(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Block was placed")
|
||||
}
|
||||
thenIdle(1)
|
||||
thenExecute { helper.assertBlockHas(BlockPos(1, 2, 3), BlockMonitor.STATE, MonitorEdgeState.LR) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can place into compostors. These are non-typical inventories, so
|
||||
* worth testing.
|
||||
*/
|
||||
@GameTest
|
||||
fun Use_compostors(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().dropDown(Optional.empty()).await()
|
||||
.assertArrayEquals(true, message = "Item was dropped")
|
||||
assertEquals(63, getApi<TurtleAPI>().getItemCount(Optional.of(1)), "Only dropped one item")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can be cleaned in cauldrons.
|
||||
*
|
||||
* Currently not required as turtles can no longer right-click cauldrons.
|
||||
*/
|
||||
@GameTest(required = false)
|
||||
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
val details = getTurtleItemDetail(1, true)
|
||||
getApi<TurtleAPI>().place(ObjectArguments()).await()
|
||||
.assertArrayEquals(true, message = "Used item on cauldron")
|
||||
val newDetails = getTurtleItemDetail(1, true)
|
||||
|
||||
assertEquals("computercraft:turtle_normal", newDetails["name"], "Still a turtle")
|
||||
assertNotEquals(details["nbt"], newDetails["nbt"], "Colour should have changed")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks turtles can use IDetailProviders by getting details for a printed page.
|
||||
*/
|
||||
@GameTest
|
||||
fun Item_detail_provider(helper: GameTestHelper) = helper.sequence {
|
||||
// Register a dummy provider for printout items
|
||||
thenExecute {
|
||||
DetailRegistries.ITEM_STACK.addProvider(
|
||||
object :
|
||||
BasicItemDetailProvider<ItemPrintout>("printout", ItemPrintout::class.java) {
|
||||
override fun provideDetails(data: MutableMap<in String, Any>, stack: ItemStack, item: ItemPrintout) {
|
||||
data["type"] = item.type.toString().lowercase()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
thenOnComputer {
|
||||
val details = getTurtleItemDetail(detailed = true)
|
||||
assertEquals(mapOf("type" to "page"), details["printout"]) {
|
||||
"Printout information is returned (whole map is $details)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advanced turtles resist all explosions but normal ones don't.
|
||||
*/
|
||||
@GameTest
|
||||
fun Resists_explosions(helper: GameTestHelper) = helper.sequence {
|
||||
thenExecute {
|
||||
val pos = helper.absolutePos(BlockPos(2, 2, 2))
|
||||
val tnt = PrimedTnt(helper.level, pos.x + 0.5, pos.y + 1.0, pos.z + 0.5, null)
|
||||
tnt.fuse = 1
|
||||
helper.level.addFreshEntity(tnt)
|
||||
}
|
||||
thenWaitUntil { helper.assertEntityNotPresent(EntityType.TNT) }
|
||||
thenExecute {
|
||||
helper.assertBlockPresent(Registry.ModBlocks.TURTLE_ADVANCED.get(), BlockPos(2, 2, 2))
|
||||
helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 1))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turtles resist mob explosions
|
||||
*/
|
||||
@GameTest
|
||||
fun Resists_entity_explosions(helper: GameTestHelper) = helper.sequence {
|
||||
thenExecute { helper.getEntity(EntityType.CREEPER).ignite() }
|
||||
thenWaitUntil { helper.assertEntityNotPresent(EntityType.CREEPER) }
|
||||
thenExecute {
|
||||
helper.assertBlockPresent(Registry.ModBlocks.TURTLE_ADVANCED.get(), BlockPos(2, 2, 2))
|
||||
helper.assertBlockPresent(Registry.ModBlocks.TURTLE_NORMAL.get(), BlockPos(2, 2, 1))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test calling `turtle.drop` into an inventory.
|
||||
*/
|
||||
@GameTest
|
||||
fun Drop_to_chest(helper: GameTestHelper) = helper.sequence {
|
||||
val turtle = BlockPos(2, 2, 2)
|
||||
val chest = BlockPos(2, 2, 3)
|
||||
|
||||
thenOnComputer {
|
||||
getApi<TurtleAPI>().drop(Optional.of(32)).await()
|
||||
.assertArrayEquals(true, message = "Could not drop items")
|
||||
}
|
||||
thenExecute {
|
||||
helper.assertContainerExactly(turtle, listOf(ItemStack(Blocks.DIRT, 32), ItemStack.EMPTY, ItemStack(Blocks.DIRT, 32)))
|
||||
helper.assertContainerExactly(chest, listOf(ItemStack(Blocks.DIRT, 48)))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Ghost peripherals?
|
||||
// TODO: Dropping into minecarts
|
||||
// TODO: Turtle sucking from items
|
||||
}
|
||||
|
||||
private suspend fun LuaTaskContext.getTurtleItemDetail(slot: Int = 1, detailed: Boolean = false): Map<String, *> {
|
||||
val item = getApi<TurtleAPI>().getItemDetail(context, Optional.of(slot), Optional.of(detailed)).await()
|
||||
assertThat("Returns details", item, array(instanceOf(Map::class.java)))
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return item!![0] as Map<String, *>
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest.api
|
||||
|
||||
import dan200.computercraft.gametest.core.ManagedComputers
|
||||
import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
|
||||
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.gametest.framework.*
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
|
||||
/**
|
||||
* Globally usable structures.
|
||||
*
|
||||
* @see GameTest.template
|
||||
*/
|
||||
object Structures {
|
||||
/** The "default" structure, a 5x5 area with a polished Andesite floor */
|
||||
const val DEFAULT = "default"
|
||||
}
|
||||
|
||||
/** Pre-set in-game times */
|
||||
object Times {
|
||||
const val NOON: Long = 6000
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom timeouts for various test types.
|
||||
*
|
||||
* @see GameTest.timeoutTicks
|
||||
*/
|
||||
object Timeouts {
|
||||
private const val SECOND: Int = 20
|
||||
|
||||
const val COMPUTER_TIMEOUT: Int = SECOND * 15
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to [GameTestSequence.thenExecute], but which won't run the next steps if the parent fails.
|
||||
*/
|
||||
fun GameTestSequence.thenExecuteFailFast(task: Runnable): GameTestSequence =
|
||||
thenExecute(task).thenWaitUntil {
|
||||
val failure = (this as GameTestSequenceAccessor).parent.error
|
||||
if (failure != null) throw failure
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until a computer has finished running and check it is OK.
|
||||
*/
|
||||
fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = ComputerState.DONE): GameTestSequence {
|
||||
val label = (this as GameTestSequenceAccessor).parent.testName + (if (name == null) "" else ".$name")
|
||||
|
||||
thenWaitUntil {
|
||||
val computer = ComputerState.get(label)
|
||||
if (computer == null || !computer.isDone(marker)) throw GameTestAssertException("Computer '$label' has not reached $marker yet.")
|
||||
}
|
||||
thenExecuteFailFast { ComputerState.get(label)!!.check(marker) }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task on a computer but don't wait for it to finish.
|
||||
*/
|
||||
fun GameTestSequence.thenStartComputer(name: String? = null, action: suspend LuaTaskContext.() -> Unit): GameTestSequence {
|
||||
val test = (this as GameTestSequenceAccessor).parent
|
||||
val label = test.testName + (if (name == null) "" else ".$name")
|
||||
return thenExecuteFailFast { ManagedComputers.enqueue(test, label, action) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a task on a computer and wait for it to finish.
|
||||
*/
|
||||
fun GameTestSequence.thenOnComputer(name: String? = null, action: suspend LuaTaskContext.() -> Unit): GameTestSequence {
|
||||
val test = (this as GameTestSequenceAccessor).parent
|
||||
val label = test.testName + (if (name == null) "" else ".$name")
|
||||
var monitor: ManagedComputers.Monitor? = null
|
||||
thenExecuteFailFast { monitor = ManagedComputers.enqueue(test, label, action) }
|
||||
thenWaitUntil { if (!monitor!!.isFinished) throw GameTestAssertException("Computer '$label' has not finished yet.") }
|
||||
thenExecuteFailFast { monitor!!.check() }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new game test sequence
|
||||
*/
|
||||
fun GameTestHelper.sequence(run: GameTestSequence.() -> Unit) {
|
||||
val sequence = startSequence()
|
||||
run(sequence)
|
||||
sequence.thenSucceed()
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom instance of [GameTestAssertPosException] which allows for longer error messages.
|
||||
*/
|
||||
private class VerboseGameTestAssertPosException(message: String, absolutePos: BlockPos, relativePos: BlockPos, tick: Long) :
|
||||
GameTestAssertPosException(message, absolutePos, relativePos, tick) {
|
||||
override fun getMessageToShowAtBlock(): String = message!!.lineSequence().first()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail this test. Unlike [GameTestHelper.fail], this trims the in-game error message to the first line.
|
||||
*/
|
||||
private fun GameTestHelper.failVerbose(message: String, pos: BlockPos): Nothing {
|
||||
throw VerboseGameTestAssertPosException(message, absolutePos(pos), pos, tick)
|
||||
}
|
||||
|
||||
/** Fail with an optional context message. */
|
||||
private fun GameTestHelper.fail(message: String?, detail: String, pos: BlockPos): Nothing {
|
||||
failVerbose(if (message.isNullOrEmpty()) detail else "$message: $detail", pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of [GameTestHelper.assertBlockState] which also includes the current block state.
|
||||
*/
|
||||
fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String = "") {
|
||||
val state = getBlockState(pos)
|
||||
if (!predicate(state)) fail(message, state.toString(), pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of [GameTestHelper.assertBlockProperty] which includes the current block state in the error message.
|
||||
*/
|
||||
fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: Property<T>, value: T, message: String = "") {
|
||||
val state = getBlockState(pos)
|
||||
if (!state.hasProperty(property)) {
|
||||
val id = ForgeRegistries.BLOCKS.getKey(state.block)
|
||||
fail(message, "block $id does not have property ${property.name}", pos)
|
||||
} else if (state.getValue(property) != value) {
|
||||
fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a container contains exactly these items and no more.
|
||||
*
|
||||
* @param pos The position of the container.
|
||||
* @param items The list of items this container must contain. This should be equal to the expected contents of the
|
||||
* first `n` slots - the remaining are required to be empty.
|
||||
*/
|
||||
fun GameTestHelper.assertContainerExactly(pos: BlockPos, items: List<ItemStack>) {
|
||||
val container = getBlockEntity(pos) ?: failVerbose("Expected a container at $pos, found nothing", pos)
|
||||
if (container !is Container) {
|
||||
failVerbose("Expected a container at $pos, found ${getName(container.type)}", pos)
|
||||
}
|
||||
|
||||
val slot = (0 until container.containerSize).indexOfFirst { slot ->
|
||||
val expected = if (slot >= items.size) ItemStack.EMPTY else items[slot]
|
||||
!ItemStack.matches(container.getItem(slot), expected)
|
||||
}
|
||||
|
||||
if (slot >= 0) {
|
||||
failVerbose(
|
||||
"""
|
||||
Items do not match (first mismatch at slot $slot).
|
||||
Expected: $items
|
||||
Container: ${(0 until container.containerSize).map { container.getItem(it) }.dropLastWhile { it.isEmpty }}
|
||||
""".trimIndent(),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getName(type: BlockEntityType<*>): ResourceLocation = ForgeRegistries.BLOCK_ENTITIES.getKey(type)!!
|
||||
|
||||
/**
|
||||
* Get a [BlockEntity] of a specific type.
|
||||
*/
|
||||
fun <T : BlockEntity> GameTestHelper.getBlockEntity(pos: BlockPos, type: BlockEntityType<T>): T {
|
||||
val tile = getBlockEntity(pos)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return when {
|
||||
tile == null -> failVerbose("Expected ${getName(type)}, but no tile was there", pos)
|
||||
tile.type != type -> failVerbose("Expected ${getName(type)} but got ${getName(tile.type)}", pos)
|
||||
else -> tile as T
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entities of a specific type within the test structure.
|
||||
*/
|
||||
fun <T : Entity> GameTestHelper.getEntities(type: EntityType<T>): List<T> {
|
||||
val info = (this as GameTestHelperAccessor).testInfo
|
||||
return level.getEntities(type, info.structureBounds!!) { it.isAlive }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an [Entity] inside the game structure, requiring there to be a single one.
|
||||
*/
|
||||
fun <T : Entity> GameTestHelper.getEntity(type: EntityType<T>): T {
|
||||
val entities = getEntities(type)
|
||||
when (entities.size) {
|
||||
0 -> throw GameTestAssertException("No $type entities")
|
||||
1 -> return entities[0]
|
||||
else -> throw GameTestAssertException("Multiple $type entities (${entities.size} in bounding box)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block within the test structure.
|
||||
*/
|
||||
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)
|
||||
|
||||
/**
|
||||
* Modify a block state within the test.
|
||||
*/
|
||||
fun GameTestHelper.modifyBlock(pos: BlockPos, modify: (BlockState) -> BlockState) {
|
||||
setBlock(pos, modify(getBlockState(pos)))
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.gametest.core
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI
|
||||
import dan200.computercraft.core.apis.OSAPI
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine
|
||||
import dan200.computercraft.core.lua.ILuaMachine
|
||||
import dan200.computercraft.core.lua.MachineEnvironment
|
||||
import dan200.computercraft.core.lua.MachineResult
|
||||
import dan200.computercraft.gametest.api.thenOnComputer
|
||||
import dan200.computercraft.mixin.gametest.GameTestInfoAccessor
|
||||
import dan200.computercraft.shared.computer.core.ServerContext
|
||||
import dan200.computercraft.test.core.computer.KotlinLuaMachine
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
import net.minecraft.gametest.framework.GameTestAssertException
|
||||
import net.minecraft.gametest.framework.GameTestAssertPosException
|
||||
import net.minecraft.gametest.framework.GameTestInfo
|
||||
import net.minecraft.gametest.framework.GameTestSequence
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/**
|
||||
* Provides a custom [ILuaMachine] which allows computers to run Kotlin or Lua code, depending on their ID.
|
||||
*
|
||||
* This allows writing game tests which consume Lua APIs, without having the overhead of starting a new computer for
|
||||
* each test.
|
||||
*
|
||||
* @see GameTestSequence.thenOnComputer
|
||||
*/
|
||||
object ManagedComputers : ILuaMachine.Factory {
|
||||
private val LOGGER = LogManager.getLogger(ManagedComputers::class.java)
|
||||
private val computers: MutableMap<String, Queue<suspend LuaTaskContext.() -> Unit>> = mutableMapOf()
|
||||
|
||||
internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor {
|
||||
val monitor = Monitor(test, label)
|
||||
computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add {
|
||||
try {
|
||||
LOGGER.info("Running $label")
|
||||
task()
|
||||
monitor.result.set(Result.success(Unit))
|
||||
} catch (e: Throwable) {
|
||||
if (e !is AssertionError) LOGGER.error("Computer $label failed", e)
|
||||
monitor.result.set(Result.failure(e))
|
||||
throw e
|
||||
} finally {
|
||||
LOGGER.info("Finished $label")
|
||||
}
|
||||
}
|
||||
|
||||
ServerContext.get(test.level.server).registry().computers
|
||||
.firstOrNull { it.label == label }?.queueEvent("test_wakeup")
|
||||
|
||||
return monitor
|
||||
}
|
||||
|
||||
override fun create(environment: MachineEnvironment): ILuaMachine = DelegateMachine(environment)
|
||||
|
||||
private class DelegateMachine(private val environment: MachineEnvironment) : ILuaMachine {
|
||||
private val apis = mutableListOf<ILuaAPI>()
|
||||
private var delegate: ILuaMachine? = null
|
||||
|
||||
override fun addAPI(api: ILuaAPI) {
|
||||
val delegate = this.delegate
|
||||
if (delegate != null) return delegate.addAPI(api)
|
||||
|
||||
apis.add(api)
|
||||
|
||||
if (api is OSAPI) {
|
||||
val newMachine = if (api.computerID != 1) {
|
||||
CobaltLuaMachine(environment)
|
||||
} else if (api.computerLabel != null) {
|
||||
KotlinMachine(environment, api.computerLabel[0] as String)
|
||||
} else {
|
||||
LOGGER.error("Kotlin Lua machine must have a label")
|
||||
CobaltLuaMachine(environment)
|
||||
}
|
||||
|
||||
this.delegate = newMachine
|
||||
for (api in apis) newMachine.addAPI(api)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadBios(bios: InputStream): MachineResult {
|
||||
val delegate = this.delegate ?: return MachineResult.error("Computer not created")
|
||||
return delegate.loadBios(bios)
|
||||
}
|
||||
|
||||
override fun handleEvent(eventName: String?, arguments: Array<out Any>?): MachineResult {
|
||||
val delegate = this.delegate ?: return MachineResult.error("Computer not created")
|
||||
return delegate.handleEvent(eventName, arguments)
|
||||
}
|
||||
|
||||
override fun printExecutionState(out: StringBuilder) {
|
||||
delegate?.printExecutionState(out)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
delegate?.close()
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinMachine(environment: MachineEnvironment, private val label: String) : KotlinLuaMachine(environment) {
|
||||
override fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? = computers[label]?.poll()
|
||||
}
|
||||
|
||||
class Monitor(private val test: GameTestInfo, private val label: String) {
|
||||
internal val result = AtomicReference<Result<Unit>>()
|
||||
|
||||
val isFinished
|
||||
get() = result.get() != null
|
||||
|
||||
fun check() {
|
||||
val result = result.get() ?: fail("Computer $label did not finish")
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) fail(error.message ?: error.toString())
|
||||
}
|
||||
|
||||
private fun fail(message: String): Nothing {
|
||||
val computer =
|
||||
ServerContext.get(test.level.server).registry().computers.firstOrNull { it.label == label }
|
||||
if (computer == null) {
|
||||
throw GameTestAssertException(message)
|
||||
} else {
|
||||
val pos = computer.position
|
||||
val relativePos = pos.subtract(test.structureBlockPos)
|
||||
throw GameTestAssertPosException(message, pos, relativePos, (test as GameTestInfoAccessor).`computercraft$getTick`())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
public net.minecraft.gametest.framework.TestCommand m_128010_(Lnet/minecraft/commands/CommandSourceStack;Ljava/lang/String;)I # exportTestStructure
|
||||
|
||||
public net.minecraft.gametest.framework.GameTestHelper m_177448_()Lnet/minecraft/world/phys/AABB; # getBounds
|
||||
public net.minecraft.gametest.framework.GameTestHelper f_127595_ # testInfo
|
||||
|
||||
public net.minecraft.gametest.framework.GameTestSequence f_127774_ # parent
|
@ -16,3 +16,10 @@ displayName="CC: Tweaked test framework"
|
||||
description='''
|
||||
A test framework for ensuring CC: Tweaked works correctly.
|
||||
'''
|
||||
|
||||
[[dependencies.cctest]]
|
||||
modId="computercraft"
|
||||
mandatory=true
|
||||
versionRange="[1.0,)"
|
||||
ordering="AFTER"
|
||||
side="BOTH"
|
||||
|
16
src/testMod/resources/computercraft-gametest.mixins.json
Normal file
16
src/testMod/resources/computercraft-gametest.mixins.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "dan200.computercraft.mixin.gametest",
|
||||
"minVersion": "0.8",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"mixins": [
|
||||
"GameTestHelperAccessor",
|
||||
"GameTestInfoAccessor",
|
||||
"GameTestSequenceAccessor",
|
||||
"GameTestSequenceMixin",
|
||||
"TestCommandAccessor"
|
||||
]
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:lever{face:floor,facing:south,powered:false}"},
|
||||
{pos: [2, 1, 1], state: "minecraft:repeater{delay:1,facing:north,locked:false,powered:false}"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:off}", nbt: {ComputerId: 0, Label: "computer_test.no_through_signal_rev", On: 0b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"},
|
||||
{pos: [2, 1, 4], state: "minecraft:redstone_lamp{lit:false}"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:redstone_lamp{lit:false}",
|
||||
"minecraft:air",
|
||||
"minecraft:lever{face:floor,facing:south,powered:false}",
|
||||
"minecraft:repeater{delay:1,facing:north,locked:false,powered:false}",
|
||||
"minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}",
|
||||
"computercraft:computer_advanced{facing:north,state:off}"
|
||||
]
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "computer_test.set_and_destroy", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:redstone_lamp{lit:false}"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:redstone_lamp{lit:false}",
|
||||
"minecraft:air",
|
||||
"computercraft:computer_advanced{facing:north,state:on}"
|
||||
]
|
||||
}
|
136
src/testMod/resources/data/cctest/structures/default.snbt
Normal file
136
src/testMod/resources/data/cctest/structures/default.snbt
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air"
|
||||
]
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
{pos: [0, 1, 1], state: "computercraft:disk_drive{facing:north,state:full}", nbt: {Item: {Count: 1b, id: "minecraft:music_disc_13"}, id: "computercraft:disk_drive"}},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "disk_drive_test.audio_disk", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [1, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "disk_drive_test.audio_disk", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
@ -43,7 +43,7 @@
|
||||
{pos: [2, 1, 3], state: "minecraft:white_stained_glass"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "disk_drive_test.ejects_disk", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "disk_drive_test.ejects_disk", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [3, 1, 2], state: "minecraft:white_stained_glass"},
|
||||
{pos: [3, 1, 3], state: "minecraft:white_stained_glass"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
@ -48,7 +48,7 @@
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.gains_peripherals", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [4, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.gains_peripherals", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [4, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
@ -49,7 +49,7 @@
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.have_peripherals", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [4, 1, 2], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.have_peripherals", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
@ -35,7 +35,7 @@
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.transmits_messages.receive", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [1, 1, 3], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.transmits_messages.receive", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
@ -43,7 +43,7 @@
|
||||
{pos: [2, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_off,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 0, Label: "modem_test.transmits_messages.send", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [3, 1, 1], state: "computercraft:computer_advanced{facing:north,state:blinking}", nbt: {ComputerId: 1, Label: "modem_test.transmits_messages.send", On: 1b, id: "computercraft:computer_advanced"}},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
@ -14,7 +14,7 @@
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 0, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "turtle_test.cleaned_with_cauldrons", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 0, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "turtle_test.cleaned_with_cauldrons", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 1], state: "minecraft:cauldron{level:3}"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:dirt"}, {Count: 32b, Slot: 2b, id: "minecraft:dirt"}], Label: "turtle_test.drop_to_chest", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:chest{facing:west,type:single,waterlogged:false}", nbt: {Items: [{Count: 16b, Slot: 0b, id: "minecraft:dirt"}], id: "minecraft:chest"}},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}",
|
||||
"minecraft:chest{facing:west,type:single,waterlogged:false}"
|
||||
]
|
||||
}
|
@ -64,7 +64,7 @@
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:bucket"}], Label: "turtle_test.gather_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:bucket"}], Label: "turtle_test.gather_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
@ -14,7 +14,7 @@
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [], Label: "turtle_test.hoe_dirt", LeftUpgrade: "minecraft:diamond_hoe", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.hoe_dirt", LeftUpgrade: "minecraft:diamond_hoe", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 1], state: "minecraft:dirt"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
@ -14,7 +14,7 @@
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:printed_page", tag: {Color0: "fffffffffffffffffffffffff", Color1: "fffffffffffffffffffffffff", Color10: "fffffffffffffffffffffffff", Color11: "fffffffffffffffffffffffff", Color12: "fffffffffffffffffffffffff", Color13: "fffffffffffffffffffffffff", Color14: "fffffffffffffffffffffffff", Color15: "fffffffffffffffffffffffff", Color16: "fffffffffffffffffffffffff", Color17: "fffffffffffffffffffffffff", Color18: "fffffffffffffffffffffffff", Color19: "fffffffffffffffffffffffff", Color2: "fffffffffffffffffffffffff", Color20: "fffffffffffffffffffffffff", Color3: "fffffffffffffffffffffffff", Color4: "fffffffffffffffffffffffff", Color5: "fffffffffffffffffffffffff", Color6: "fffffffffffffffffffffffff", Color7: "fffffffffffffffffffffffff", Color8: "fffffffffffffffffffffffff", Color9: "fffffffffffffffffffffffff", Pages: 1, Text0: "Example ", Text1: " ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "Example page"}}], Label: "turtle_test.item_detail_provider", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:printed_page", tag: {Color0: "fffffffffffffffffffffffff", Color1: "fffffffffffffffffffffffff", Color10: "fffffffffffffffffffffffff", Color11: "fffffffffffffffffffffffff", Color12: "fffffffffffffffffffffffff", Color13: "fffffffffffffffffffffffff", Color14: "fffffffffffffffffffffffff", Color15: "fffffffffffffffffffffffff", Color16: "fffffffffffffffffffffffff", Color17: "fffffffffffffffffffffffff", Color18: "fffffffffffffffffffffffff", Color19: "fffffffffffffffffffffffff", Color2: "fffffffffffffffffffffffff", Color20: "fffffffffffffffffffffffff", Color3: "fffffffffffffffffffffffff", Color4: "fffffffffffffffffffffffff", Color5: "fffffffffffffffffffffffff", Color6: "fffffffffffffffffffffffff", Color7: "fffffffffffffffffffffffff", Color8: "fffffffffffffffffffffffff", Color9: "fffffffffffffffffffffffff", Pages: 1, Text0: "Example ", Text1: " ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "Example page"}}], Label: "turtle_test.item_detail_provider", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
@ -64,7 +64,7 @@
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:lava_bucket"}], Label: "turtle_test.place_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:lava_bucket"}], Label: "turtle_test.place_lava", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
@ -34,7 +34,7 @@
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:monitor_advanced"}], Label: "turtle_test.place_monitor", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:monitor_advanced"}], Label: "turtle_test.place_monitor", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:dark_oak_planks"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
@ -38,7 +38,7 @@
|
||||
{pos: [1, 1, 3], state: "minecraft:white_stained_glass"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:white_stained_glass"},
|
||||
{pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:true}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:oak_fence"}], Label: "turtle_test.place_waterlogged", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:true}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:oak_fence"}], Label: "turtle_test.place_waterlogged", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 2], state: "minecraft:water{level:0}"},
|
||||
{pos: [2, 1, 3], state: "minecraft:white_stained_glass"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
@ -0,0 +1,140 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [
|
||||
{blockPos: [1, 1, 2], pos: [1.5d, 1.0d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.0d, Name: "forge:step_height_addition"}, {Base: 0.25d, Name: "minecraft:generic.movement_speed"}, {Base: 0.08d, Name: "forge:entity_gravity"}, {Base: 16.0d, Modifiers: [{Amount: -0.0871987524284032d, Name: "Random spawn bonus", Operation: 1, UUID: [I; -1592956383, -599506679, -1812844190, 1076877318]}], Name: "minecraft:generic.follow_range"}], Brain: {memories: {}}, CanPickUpLoot: 0b, CanUpdate: 1b, DeathTime: 0s, ExplosionRadius: 3b, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, Fuse: 30s, HandDropChances: [0.085f, 0.085f], HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invulnerable: 0b, LeftHanded: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], OnGround: 1b, PersistenceRequired: 0b, PortalCooldown: 0, Pos: [2.5d, -58.0d, 3.5d], Rotation: [143.85756f, 0.0f], UUID: [I; 1463798444, -662876850, -1423329658, 948503391], id: "minecraft:creeper", ignited: 0b}}
|
||||
],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}",
|
||||
"computercraft:turtle_advanced{facing:south,waterlogged:false}"
|
||||
]
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
{
|
||||
DataVersion: 3120,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 1, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 2], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [0, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 0], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 1], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 2], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 3], state: "minecraft:barrier"},
|
||||
{pos: [4, 2, 4], state: "minecraft:barrier"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:barrier",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}",
|
||||
"computercraft:turtle_advanced{facing:south,waterlogged:false}"
|
||||
]
|
||||
}
|
@ -89,7 +89,7 @@
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:shears", tag: {Damage: 0}}], Label: "turtle_test.shears_sheep", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 3, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "minecraft:shears", tag: {Damage: 0}}], Label: "turtle_test.shears_sheep", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
@ -39,7 +39,7 @@
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [], Label: "turtle_test.unequip_refreshes_peripheral", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, RightUpgrade: "computercraft:wireless_modem_normal", RightUpgradeNbt: {active: 0b}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.unequip_refreshes_peripheral", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, RightUpgrade: "computercraft:wireless_modem_normal", RightUpgradeNbt: {active: 0b}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
@ -64,7 +64,7 @@
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 0, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:spruce_sapling"}], Label: "turtle_test.use_compostors", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 64b, Slot: 0b, id: "minecraft:spruce_sapling"}], Label: "turtle_test.use_compostors", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"pools": [
|
||||
{
|
||||
"name": "main",
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "computercraft:treasure_disk",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:set_nbt",
|
||||
"tag": "{\"Title\": \"Demo disk\", \"SubPath\": \"demo\", \"Colour\": 15905331}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
test.eq(true, disk.hasAudio("right"), "Has audio")
|
||||
test.eq("C418 - 13", disk.getAudioTitle("right"), "Audio title")
|
@ -1 +0,0 @@
|
||||
disk.eject("right")
|
@ -1,18 +0,0 @@
|
||||
local function check_peripherals(expected, msg)
|
||||
local peripherals = peripheral.getNames()
|
||||
table.sort(peripherals)
|
||||
|
||||
test.eq(table.concat(expected, ", "), table.concat(peripherals, ", "), msg)
|
||||
end
|
||||
|
||||
check_peripherals({"back"}, "Has no peripherals on startup")
|
||||
test.ok("initial")
|
||||
|
||||
os.pullEvent("peripheral")
|
||||
sleep(0)
|
||||
|
||||
check_peripherals({
|
||||
"back",
|
||||
"monitor_1",
|
||||
"printer_1",
|
||||
}, "Gains new peripherals")
|
@ -1,12 +0,0 @@
|
||||
local function check_peripherals(expected, msg)
|
||||
local peripherals = peripheral.getNames()
|
||||
table.sort(peripherals)
|
||||
|
||||
test.eq(table.concat(expected, ", "), table.concat(peripherals, ", "), msg)
|
||||
end
|
||||
|
||||
check_peripherals({
|
||||
"monitor_0",
|
||||
"printer_0",
|
||||
"right",
|
||||
}, "Starts with peripherals")
|
@ -1,10 +0,0 @@
|
||||
local modem = peripheral.find("modem")
|
||||
modem.open(12)
|
||||
|
||||
|
||||
local _, name, chan, reply, payload, distance = os.pullEvent("modem_message")
|
||||
test.eq("left", name, "Modem name")
|
||||
test.eq(12, chan, "Channel")
|
||||
test.eq(34, reply, "Reply channel")
|
||||
test.eq("Hello!", payload, "Payload")
|
||||
test.eq(4, distance, "Distance") -- Why 4?!
|
@ -1,5 +0,0 @@
|
||||
local modem = peripheral.find("modem")
|
||||
while true do
|
||||
modem.transmit(12, 34, "Hello!")
|
||||
sleep(1)
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
local old_details = turtle.getItemDetail(1, true)
|
||||
|
||||
test.assert(turtle.place(), "Dyed turtle")
|
||||
|
||||
local new_details = turtle.getItemDetail(1, true)
|
||||
test.eq("computercraft:turtle_normal", new_details.name, "Still a turtle")
|
||||
test.neq(old_details.nbt, new_details.nbt, "Colour has changed")
|
@ -1,7 +0,0 @@
|
||||
turtle.placeDown()
|
||||
|
||||
local item = turtle.getItemDetail()
|
||||
test.eq("minecraft:lava_bucket", item.name)
|
||||
|
||||
local has_down, down = turtle.inspectDown()
|
||||
test.eq(false, has_down, "Air below")
|
@ -1,5 +0,0 @@
|
||||
test.assert(turtle.dig())
|
||||
|
||||
local has_block, block = turtle.inspect()
|
||||
test.assert(has_block, "Has block")
|
||||
test.eq("minecraft:farmland", block.name)
|
@ -1,7 +0,0 @@
|
||||
test.ok("initial")
|
||||
|
||||
local details = turtle.getItemDetail(1, true)
|
||||
|
||||
test.assert(details, "Has details")
|
||||
test.assert(details.printout, "Has printout meta")
|
||||
test.eq("PAGE", details.printout.type)
|
@ -1,5 +0,0 @@
|
||||
test.assert(turtle.placeDown())
|
||||
|
||||
local ok, down = turtle.inspectDown()
|
||||
test.assert(ok, "Has below")
|
||||
test.eq("minecraft:lava", down.name, "Is lava")
|
@ -1,6 +0,0 @@
|
||||
test.assert(turtle.place())
|
||||
|
||||
local has_block, block = turtle.inspect()
|
||||
test.assert(has_block, "Has block")
|
||||
test.eq("computercraft:monitor_advanced", block.name)
|
||||
test.eq("lr", block.state.state)
|
@ -1,6 +0,0 @@
|
||||
test.assert(turtle.place())
|
||||
|
||||
local has_block, block = turtle.inspect()
|
||||
test.eq(true, has_block, "Has block")
|
||||
test.eq("minecraft:oak_fence", block.name)
|
||||
test.eq(true, block.state.waterlogged)
|
@ -1,9 +0,0 @@
|
||||
-- TurtleTest.`Shears sheep`
|
||||
|
||||
turtle.placeDown()
|
||||
|
||||
local item = turtle.getItemDetail(2)
|
||||
if item == nil then test.fail("Got no item") end
|
||||
test.eq("minecraft:white_wool", item.name)
|
||||
|
||||
test.ok()
|
@ -1,3 +0,0 @@
|
||||
test.eq("modem", peripheral.getType("right"), "Starts with a modem")
|
||||
turtle.equipRight()
|
||||
test.eq("drive", peripheral.getType("right"), "Unequipping gives a drive")
|
@ -1 +0,0 @@
|
||||
test.eq(true, turtle.dropDown(), "Drop items into compostor")
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"computer": 0,
|
||||
"peripheral.monitor": 1,
|
||||
"peripheral.printer": 1
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
# Automatically generated EULA. Please don't use this for a real server.
|
||||
eula=true
|
Binary file not shown.
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
Before Width: | Height: | Size: 113 KiB |
Binary file not shown.
Before Width: | Height: | Size: 44 KiB |
@ -1,45 +0,0 @@
|
||||
# Minecraft server properties
|
||||
allow-flight=false
|
||||
allow-nether=true
|
||||
broadcast-console-to-ops=true
|
||||
broadcast-rcon-to-ops=true
|
||||
difficulty=easy
|
||||
enable-command-block=true
|
||||
enable-query=false
|
||||
enable-rcon=false
|
||||
enforce-whitelist=false
|
||||
force-gamemode=false
|
||||
function-permission-level=2
|
||||
gamemode=creative
|
||||
generate-structures=false
|
||||
generator-settings=
|
||||
hardcore=false
|
||||
level-name=world
|
||||
level-seed=
|
||||
level-type=flat
|
||||
max-build-height=256
|
||||
max-players=20
|
||||
max-tick-time=60000
|
||||
max-world-size=29999984
|
||||
motd=A testing server
|
||||
network-compression-threshold=256
|
||||
online-mode=false
|
||||
op-permission-level=4
|
||||
player-idle-timeout=0
|
||||
prevent-proxy-connections=false
|
||||
pvp=true
|
||||
query.port=25565
|
||||
rcon.password=
|
||||
rcon.port=25575
|
||||
resource-pack=
|
||||
resource-pack-sha1=
|
||||
server-ip=
|
||||
server-port=25565
|
||||
snooper-enabled=true
|
||||
spawn-animals=true
|
||||
spawn-monsters=true
|
||||
spawn-npcs=true
|
||||
spawn-protection=16
|
||||
use-native-transport=true
|
||||
view-distance=10
|
||||
white-list=false
|
Loading…
Reference in New Issue
Block a user