├── src └── main │ ├── resources │ ├── mctester.accesswidener │ ├── assets │ │ └── mctester │ │ │ └── icon.png │ ├── fabric.mod.json │ └── mctester.mixins.json │ └── java │ └── mctester │ ├── common │ ├── test │ │ ├── exceptions │ │ │ ├── GameTestAssertException.java │ │ │ └── PreconditionNotMetException.java │ │ └── creation │ │ │ ├── TestConfig.java │ │ │ └── GameTestHelper.java │ └── util │ │ ├── TestFunctionWithVariant.java │ │ ├── BlockRotationUtil.java │ │ ├── StructureNBTConverter.java │ │ └── GameTestUtil.java │ ├── mixin │ ├── accessor │ │ ├── GameTestStateAccessor.java │ │ ├── TestContextAccessor.java │ │ ├── GoalSelectorAccessor.java │ │ ├── TestFunctionAccessor.java │ │ ├── MobEntityAccessor.java │ │ └── BrainAccessor.java │ ├── fixes │ │ ├── function_lookup │ │ │ └── TestCommandMixin.java │ │ ├── startup_crash │ │ │ └── MainMixin.java │ │ └── area_clearing │ │ │ └── StructureTestUtilMixin.java │ ├── startup │ │ ├── SharedConstantsMixin.java │ │ └── Autostart_MinecraftServerMixin.java │ ├── enable_testing │ │ ├── StructureTemplateManagerMixin.java │ │ ├── MinecraftServerMixin.java │ │ ├── ArgumentTypesMixin.java │ │ └── CommandManagerMixin.java │ ├── variants │ │ ├── TestFunctionMixin.java │ │ └── TestFunctionsMixin.java │ ├── GameTestStateMixin.java │ ├── test_function_templates │ │ └── TestFunctionsMixin.java │ └── after_test │ │ ├── TestCommandMixin.java │ │ └── crash_exit_code_fix │ │ └── MinecraftServerMixin.java │ ├── annotation │ ├── GameTestTemplate.java │ ├── GameTestExtra.java │ └── TestRegistryHelper.java │ ├── tests │ ├── Minecarts.java │ ├── BoxFill.java │ ├── MobAi.java │ └── Example.java │ ├── McTesterMod.java │ ├── templates │ └── TestRedstoneTemplate.java │ └── McTesterConfig.java ├── run ├── world │ └── level.dat ├── config │ └── mctester.properties └── gameteststructures │ ├── example.cow_on_cactus.snbt │ ├── boxfill.fillall.snbt │ ├── example.wolf_skeleton_fight.snbt │ ├── minecarts.turn.snbt │ ├── test_redstone.redstone2.snbt │ ├── test_redstone.redstone1.snbt │ ├── example.waterflow1.snbt │ ├── test_redstone.redstone4.snbt │ ├── test_redstone.redstone3.snbt │ ├── test_redstone.redstone5.snbt │ └── test_redstone_hoppercart.snbt ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── jitpack.yml ├── settings.gradle ├── gradle.properties ├── .gitignore ├── .github └── workflows │ └── build.yml ├── gradlew.bat ├── README.md ├── LICENSE └── gradlew /src/main/resources/mctester.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | 3 | -------------------------------------------------------------------------------- /run/world/level.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2No2Name/McTester/HEAD/run/world/level.dat -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2No2Name/McTester/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/mctester/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2No2Name/McTester/HEAD/src/main/resources/assets/mctester/icon.png -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 3 | before_install: 4 | - export SDKMAN_DIR="/home/jitpack/.sdkman/" 5 | - source "/home/jitpack/.sdkman/bin/sdkman-init.sh" 6 | - sdk install java 21.0.2-tem 7 | - sdk use java 21.0.2-tem -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /run/config/mctester.properties: -------------------------------------------------------------------------------- 1 | isDevelopment=true 2 | autostart=serveronly 3 | autostart.shuffle=true 4 | #autostart.shuffle.seed= 5 | crashOnFail=serveronly 6 | shutdownAfterTest=serveronly 7 | stayUpAfterFail=false 8 | includeExampleTests=true -------------------------------------------------------------------------------- /src/main/java/mctester/common/test/exceptions/GameTestAssertException.java: -------------------------------------------------------------------------------- 1 | package mctester.common.test.exceptions; 2 | 3 | public class GameTestAssertException extends RuntimeException { 4 | public GameTestAssertException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/GameTestStateAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.test.GameTestState; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | 6 | @Mixin(GameTestState.class) 7 | public interface GameTestStateAccessor { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mctester/annotation/GameTestTemplate.java: -------------------------------------------------------------------------------- 1 | package mctester.annotation; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface GameTestTemplate { 8 | String name(); 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/fixes/function_lookup/TestCommandMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.fixes.function_lookup; 2 | 3 | import net.minecraft.server.command.TestCommand; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | 6 | @Mixin(TestCommand.class) 7 | public class TestCommandMixin { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/util/TestFunctionWithVariant.java: -------------------------------------------------------------------------------- 1 | package mctester.common.util; 2 | 3 | import org.spongepowered.asm.mixin.Unique; 4 | 5 | public interface TestFunctionWithVariant { 6 | @Unique 7 | int mcTester$getVariant(); 8 | 9 | @Unique 10 | void mcTester$setVariant(int variant); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/test/exceptions/PreconditionNotMetException.java: -------------------------------------------------------------------------------- 1 | package mctester.common.test.exceptions; 2 | 3 | public class PreconditionNotMetException extends RuntimeException { 4 | public static final PreconditionNotMetException INSTANCE = new PreconditionNotMetException(); 5 | 6 | private PreconditionNotMetException() { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | # Fabric Properties 4 | # check these on https://modmuss50.me/fabric.html 5 | minecraft_version=1.20.5-pre1 6 | yarn_mappings=1.20.5-pre1+build.5 7 | loader_version=0.15.9 8 | # Mod Properties 9 | mod_version=0.4.3 10 | maven_group=mctester 11 | archives_base_name=mc-tester -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/TestContextAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.test.GameTestState; 4 | import net.minecraft.test.TestContext; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(TestContext.class) 9 | public interface TestContextAccessor { 10 | @Accessor("test") 11 | GameTestState getTest(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mctester/annotation/GameTestExtra.java: -------------------------------------------------------------------------------- 1 | package mctester.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(value={ElementType.METHOD}) 9 | @Retention(value= RetentionPolicy.RUNTIME) 10 | public @interface GameTestExtra { 11 | int variants() default 1; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/GoalSelectorAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.entity.ai.goal.GoalSelector; 4 | import net.minecraft.entity.ai.goal.PrioritizedGoal; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.Set; 9 | 10 | @Mixin(GoalSelector.class) 11 | public interface GoalSelectorAccessor { 12 | @Accessor("goals") 13 | Set getGoals(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/TestFunctionAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.test.TestContext; 4 | import net.minecraft.test.TestFunction; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.function.Consumer; 9 | 10 | @Mixin(TestFunction.class) 11 | public interface TestFunctionAccessor { 12 | 13 | @Accessor("starter") 14 | Consumer getStarter(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/MobEntityAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.entity.ai.goal.GoalSelector; 4 | import net.minecraft.entity.mob.MobEntity; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(MobEntity.class) 9 | public interface MobEntityAccessor { 10 | @Accessor 11 | GoalSelector getTargetSelector(); 12 | 13 | @Accessor 14 | GoalSelector getGoalSelector(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | 34 | # relevant files in the run directory 35 | run/* 36 | !run/gameteststructures/ 37 | !run/world/ 38 | run/world/* 39 | !run/world/level.dat 40 | !run/config 41 | run/config/* 42 | !run/config/mctester.properties -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/startup/SharedConstantsMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.startup; 2 | 3 | import mctester.McTesterConfig; 4 | import net.minecraft.SharedConstants; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | @Mixin(SharedConstants.class) 9 | public class SharedConstantsMixin { 10 | @Shadow 11 | public static boolean isDevelopment; 12 | 13 | static { 14 | if (McTesterConfig.isDevelopment()) { 15 | isDevelopment = true; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mctester/tests/Minecarts.java: -------------------------------------------------------------------------------- 1 | package mctester.tests; 2 | 3 | import mctester.common.test.creation.GameTestHelper; 4 | import net.minecraft.test.GameTest; 5 | import net.minecraft.test.TestContext; 6 | 7 | import static net.minecraft.entity.EntityType.MINECART; 8 | 9 | public class Minecarts { 10 | @GameTest 11 | public static void turn(TestContext context) { 12 | GameTestHelper helper = GameTestHelper.get(context); 13 | helper.pressButton(0, 3, 1); 14 | helper.succeedWhenEntityPresent(MINECART, 3, 2, 2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/accessor/BrainAccessor.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.accessor; 2 | 3 | import net.minecraft.entity.LivingEntity; 4 | import net.minecraft.entity.ai.brain.Activity; 5 | import net.minecraft.entity.ai.brain.Brain; 6 | import net.minecraft.entity.ai.brain.task.Task; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | @Mixin(Brain.class) 14 | public interface BrainAccessor { 15 | @Accessor("tasks") 16 | Map>>> getTasks(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/enable_testing/StructureTemplateManagerMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.enable_testing; 2 | 3 | import net.minecraft.structure.StructureTemplateManager; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(StructureTemplateManager.class) 9 | public class StructureTemplateManagerMixin { 10 | @Redirect( 11 | method = "", 12 | at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z") 13 | ) 14 | private boolean enableTests() { 15 | return true; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/variants/TestFunctionMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.variants; 2 | 3 | import mctester.common.util.TestFunctionWithVariant; 4 | import net.minecraft.test.TestFunction; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | 8 | @Mixin(TestFunction.class) 9 | public class TestFunctionMixin implements TestFunctionWithVariant { 10 | 11 | @Unique 12 | private int variant; 13 | 14 | @Override 15 | @Unique 16 | public int mcTester$getVariant() { 17 | return variant; 18 | } 19 | 20 | @Override 21 | @Unique 22 | public void mcTester$setVariant(int variant) { 23 | this.variant = variant; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/enable_testing/MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.enable_testing; 2 | 3 | import net.minecraft.server.MinecraftServer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | import java.util.function.BooleanSupplier; 9 | 10 | @Mixin(MinecraftServer.class) 11 | public class MinecraftServerMixin { 12 | 13 | @Redirect( 14 | method = "tickWorlds", 15 | at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z") 16 | ) 17 | private boolean enableTests(BooleanSupplier shouldKeepTicking) { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/enable_testing/ArgumentTypesMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.enable_testing; 2 | 3 | import net.minecraft.command.argument.ArgumentTypes; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(ArgumentTypes.class) 9 | public class ArgumentTypesMixin { 10 | @Redirect( 11 | method = "register(Lnet/minecraft/registry/Registry;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;", 12 | at = @At(value = "FIELD", target = "Lnet/minecraft/SharedConstants;isDevelopment:Z") 13 | ) 14 | private static boolean enableTests() { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mctester", 4 | "version": "${version}", 5 | 6 | "name": "MC Tester Mod", 7 | "description": "This is an example description! Tell everyone what your mod is about!", 8 | "authors": [ 9 | "2No2Name" 10 | ], 11 | "contact": { 12 | "homepage": "https://github.com/2No2Name", 13 | "sources": "https://github.com/2No2Name" 14 | }, 15 | 16 | "icon": "assets/mctester/icon.png", 17 | 18 | "environment": "*", 19 | "entrypoints": { 20 | "main": [ 21 | "mctester.McTesterMod" 22 | ] 23 | }, 24 | "mixins": [ 25 | "mctester.mixins.json" 26 | ], 27 | "depends": { 28 | "fabricloader": ">=0.7.4", 29 | "minecraft": "*" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/mctester/tests/BoxFill.java: -------------------------------------------------------------------------------- 1 | package mctester.tests; 2 | 3 | import mctester.common.test.creation.GameTestHelper; 4 | import mctester.common.util.GameTestUtil; 5 | import net.minecraft.block.Blocks; 6 | import net.minecraft.test.GameTest; 7 | import net.minecraft.test.TestContext; 8 | 9 | public class BoxFill { 10 | @GameTest( 11 | rotation = 5 12 | ) 13 | public static void fillall(TestContext context) { 14 | GameTestHelper helper = GameTestHelper.get(context); 15 | GameTestUtil.streamPositions(helper.gameTest).forEach(blockPos -> 16 | helper.gameTest.getWorld().setBlockState(blockPos, Blocks.STONE.getDefaultState())); 17 | helper.succeedWhen( 18 | helper1 -> true 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/enable_testing/CommandManagerMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.enable_testing; 2 | 3 | import net.minecraft.server.command.CommandManager; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(CommandManager.class) 9 | public class CommandManagerMixin { 10 | 11 | @Redirect( 12 | method = "", 13 | at = @At( 14 | value = "FIELD", 15 | target = "Lnet/minecraft/SharedConstants;isDevelopment:Z", 16 | ordinal = 0 17 | ) 18 | ) 19 | private boolean enableTestCommmand(CommandManager.RegistrationEnvironment environment) { 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/fixes/startup_crash/MainMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.fixes.startup_crash; 2 | 3 | import net.minecraft.server.Main; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | @Mixin(Main.class) 10 | public class MainMixin { 11 | 12 | @Inject( 13 | method = "main", remap = false, 14 | at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Lorg/slf4j/Marker;Ljava/lang/String;Ljava/lang/Throwable;)V") 15 | ) 16 | private static void crashServerStartup(String[] args, CallbackInfo ci) { 17 | throw new IllegalStateException("Server startup failed!"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/fixes/area_clearing/StructureTestUtilMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.fixes.area_clearing; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.entity.Entity; 5 | import net.minecraft.entity.player.PlayerEntity; 6 | import net.minecraft.server.world.ServerWorld; 7 | import net.minecraft.test.StructureTestUtil; 8 | import net.minecraft.util.math.BlockBox; 9 | import net.minecraft.util.math.Box; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | import java.util.List; 16 | 17 | @Mixin(StructureTestUtil.class) 18 | public class StructureTestUtilMixin { 19 | 20 | @Inject( 21 | method = "clearArea", at = @At("RETURN") 22 | ) 23 | private static void clearEntitiesAgain(BlockBox area, ServerWorld world, CallbackInfo ci, @Local Box box) { 24 | List list = world.getEntitiesByClass(Entity.class, box, entity -> !(entity instanceof PlayerEntity)); 25 | list.forEach(e -> e.remove(Entity.RemovalReason.CHANGED_DIMENSION)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/mctester.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "mctester.mixin", 5 | "compatibilityLevel": "JAVA_16", 6 | "mixins": [ 7 | "GameTestStateMixin", 8 | "accessor.BrainAccessor", 9 | "accessor.GameTestStateAccessor", 10 | "accessor.GoalSelectorAccessor", 11 | "accessor.MobEntityAccessor", 12 | "accessor.TestContextAccessor", 13 | "accessor.TestFunctionAccessor", 14 | "after_test.TestCommandMixin", 15 | "after_test.crash_exit_code_fix.MinecraftServerMixin", 16 | "enable_testing.ArgumentTypesMixin", 17 | "enable_testing.CommandManagerMixin", 18 | "enable_testing.MinecraftServerMixin", 19 | "enable_testing.StructureTemplateManagerMixin", 20 | "fixes.area_clearing.StructureTestUtilMixin", 21 | "fixes.function_lookup.TestCommandMixin", 22 | "fixes.startup_crash.MainMixin", 23 | "startup.Autostart_MinecraftServerMixin", 24 | "startup.SharedConstantsMixin", 25 | "test_function_templates.TestFunctionsMixin", 26 | "variants.TestFunctionMixin", 27 | "variants.TestFunctionsMixin" 28 | ], 29 | "client": [ 30 | ], 31 | "injectors": { 32 | "defaultRequire": 1 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/util/BlockRotationUtil.java: -------------------------------------------------------------------------------- 1 | package mctester.common.util; 2 | 3 | import net.minecraft.util.BlockRotation; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class BlockRotationUtil { 7 | private static final BlockRotation[] INVERSES = {BlockRotation.NONE, BlockRotation.COUNTERCLOCKWISE_90, BlockRotation.CLOCKWISE_180, BlockRotation.CLOCKWISE_90}; 8 | 9 | static { 10 | for (BlockRotation blockRotation : BlockRotation.values()) { 11 | BlockRotation inverse = INVERSES[blockRotation.ordinal()]; 12 | if (blockRotation.rotate(inverse) != BlockRotation.NONE) { 13 | throw new AssertionError(); 14 | } 15 | } 16 | } 17 | 18 | public static BlockRotation inverseOf(@NotNull BlockRotation blockRotation) { 19 | return INVERSES[blockRotation.ordinal()]; 20 | } 21 | 22 | public static boolean isCustom(int rotation) { 23 | return rotation < 0 || rotation >= BlockRotation.values().length; 24 | } 25 | 26 | public static BlockRotation[] getRotations(int rotations) { 27 | if (isCustom(rotations)) { 28 | //Only implemented all rotations at the same time for now. 29 | return BlockRotation.values(); 30 | } 31 | return new BlockRotation[] {BlockRotation.values()[rotations]}; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 15 | 21 # Minimum supported by Minecraft 16 | ] 17 | os: [ ubuntu-latest ] # only run on linux as we expect no difference by platform anyway 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - name: checkout repository 21 | uses: actions/checkout@v2 22 | - name: validate gradle wrapper 23 | uses: gradle/wrapper-validation-action@v1 24 | - name: setup jdk ${{ matrix.java }} 25 | uses: actions/setup-java@v2 26 | with: 27 | distribution: adopt 28 | java-version: ${{ matrix.java }} 29 | - name: make gradle wrapper executable 30 | if: ${{ runner.os != 'Windows' }} 31 | run: chmod +x ./gradlew 32 | - name: build 33 | run: ./gradlew build 34 | - name: run test server 35 | timeout-minutes: 5 36 | run: ./gradlew runServer 37 | - name: capture build artifacts 38 | uses: actions/upload-artifact@v2 39 | with: 40 | name: Artifacts 41 | path: build/libs/ 42 | -------------------------------------------------------------------------------- /src/main/java/mctester/McTesterMod.java: -------------------------------------------------------------------------------- 1 | package mctester; 2 | 3 | import mctester.annotation.TestRegistryHelper; 4 | import mctester.common.util.StructureNBTConverter; 5 | import mctester.templates.TestRedstoneTemplate; 6 | import mctester.tests.BoxFill; 7 | import mctester.tests.Example; 8 | import mctester.tests.Minecarts; 9 | import mctester.tests.MobAi; 10 | import net.fabricmc.api.ModInitializer; 11 | 12 | public class McTesterMod implements ModInitializer { 13 | @Override 14 | public void onInitialize() { 15 | // This code runs as soon as Minecraft is in a mod-load-ready state. 16 | // However, some things (like resources) may still be uninitialized. 17 | // Proceed with mild caution. 18 | 19 | StructureNBTConverter.convertAllNbtToSnbt(); 20 | 21 | if (McTesterConfig.shouldIncludeExampleTemplates()) { 22 | TestRegistryHelper.createTestTemplateFromClass(TestRedstoneTemplate.class); 23 | } 24 | 25 | if (McTesterConfig.shouldIncludeExampleTests()) { 26 | boolean tmp = TestRegistryHelper.shouldWarnOnMissingStructureFile; 27 | TestRegistryHelper.shouldWarnOnMissingStructureFile = false; 28 | TestRegistryHelper.createTestsFromClass(Example.class); 29 | TestRegistryHelper.createTestsFromClass(MobAi.class); 30 | TestRegistryHelper.createTestsFromClass(Minecarts.class); 31 | TestRegistryHelper.createTestsFromClass(BoxFill.class); 32 | TestRegistryHelper.shouldWarnOnMissingStructureFile = tmp; 33 | } 34 | 35 | TestRegistryHelper.createTemplatedTestsFromFiles(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/util/StructureNBTConverter.java: -------------------------------------------------------------------------------- 1 | package mctester.common.util; 2 | 3 | import net.minecraft.data.DataWriter; 4 | import net.minecraft.data.dev.NbtProvider; 5 | import net.minecraft.test.StructureTestUtil; 6 | 7 | import java.io.File; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | 11 | public class StructureNBTConverter { 12 | /** 13 | * Convenience method to allow the user to put normal structure block nbt files into the gameteststructures folder. 14 | */ 15 | public static void convertAllNbtToSnbt() { 16 | String structuresDirectoryName = StructureTestUtil.testStructuresDirectoryName; 17 | File[] files = new File(structuresDirectoryName).listFiles(); 18 | if (files == null) { 19 | return; 20 | } 21 | for (File file : files) { 22 | if (file == null) { 23 | continue; 24 | } 25 | String fileName = file.getName(); 26 | if (!file.isFile() || !file.canRead() || !fileName.endsWith(".nbt")) { 27 | continue; 28 | } 29 | String structureName = fileName.substring(0, fileName.length() - ".nbt".length()); 30 | 31 | Path path = NbtProvider.convertNbtToSnbt(DataWriter.UNCACHED, file.toPath(), structureName, Paths.get(structuresDirectoryName)); 32 | if (path != null) { 33 | //delete nbt file after successfully converting to snbt 34 | //noinspection ResultOfMethodCallIgnored 35 | file.delete(); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/GameTestStateMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin; 2 | 3 | import mctester.common.test.creation.GameTestHelper; 4 | import net.minecraft.test.GameTestState; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.Unique; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | import java.util.function.LongConsumer; 13 | 14 | @Mixin(GameTestState.class) 15 | public class GameTestStateMixin implements GameTestHelper.GameTestAccess { 16 | @Unique 17 | private final GameTestHelper gameTestHelper = new GameTestHelper((GameTestState) (Object) this); 18 | @Shadow 19 | private long tick; 20 | @Unique 21 | private LongConsumer tickCallback; 22 | 23 | @Override 24 | public GameTestHelper mcTester$getGameTestHelper() { 25 | return this.gameTestHelper; 26 | } 27 | 28 | @Override 29 | public void mcTester$setTickCallback(LongConsumer handler) { 30 | this.tickCallback = handler; 31 | } 32 | 33 | @Inject( 34 | method = "tickTests()V", 35 | at = @At( 36 | value = "INVOKE", 37 | target = "Lit/unimi/dsi/fastutil/objects/Object2LongMap;object2LongEntrySet()Lit/unimi/dsi/fastutil/objects/ObjectSet;", 38 | shift = At.Shift.BEFORE, 39 | remap = false 40 | ) 41 | ) 42 | private void tickGameTestHelper(CallbackInfo ci) { 43 | this.tickCallback.accept(this.tick); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/mctester/tests/MobAi.java: -------------------------------------------------------------------------------- 1 | package mctester.tests; 2 | 3 | import mctester.annotation.GameTestExtra; 4 | import mctester.common.test.creation.GameTestHelper; 5 | import mctester.common.test.exceptions.GameTestAssertException; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.mob.MobEntity; 8 | import net.minecraft.test.GameTest; 9 | import net.minecraft.test.TestContext; 10 | import net.minecraft.util.math.BlockPos; 11 | 12 | import static net.minecraft.entity.EntityType.*; 13 | 14 | public class MobAi { 15 | private static final EntityType[] MOB_TYPES = {VILLAGER, PIG, CAVE_SPIDER, CREEPER}; 16 | 17 | @GameTest(tickLimit = 200, required = false, templateName = "fire_maze") 18 | @GameTestExtra(variants = 4) 19 | public static void fire_maze(TestContext context) { 20 | GameTestHelper helper = GameTestHelper.get(context); 21 | int variant = helper.getVariant(); 22 | //noinspection unchecked 23 | EntityType entityType = (EntityType) MOB_TYPES[variant]; 24 | MobEntity mob = helper.spawnWithNoFreeWill(entityType, 1, 2, 1); 25 | 26 | final BlockPos targetPos = new BlockPos(10, 2, 5); 27 | helper.walkTo(mob, targetPos); 28 | 29 | helper.succeedWhen(() -> { 30 | helper.assertEntityPresent(entityType, targetPos); 31 | if (mob.isOnFire()) { 32 | throw new GameTestAssertException("Mob should not be burning!"); 33 | } 34 | if (mob.getHealth() < mob.getMaxHealth()) { 35 | throw new GameTestAssertException("Mob should not be hurt!"); 36 | } 37 | return true; 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/test_function_templates/TestFunctionsMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.test_function_templates; 2 | 3 | import mctester.annotation.TestRegistryHelper; 4 | import mctester.common.test.creation.TestConfig; 5 | import net.minecraft.test.TestFunction; 6 | import net.minecraft.test.TestFunctions; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | import java.util.List; 13 | import java.util.Optional; 14 | import java.util.function.Function; 15 | import java.util.stream.Stream; 16 | 17 | @Mixin(TestFunctions.class) 18 | public class TestFunctionsMixin { 19 | 20 | @Inject( 21 | method = "getTestFunction(Ljava/lang/String;)Ljava/util/Optional;", 22 | at = @At("RETURN"), 23 | cancellable = true 24 | ) 25 | private static void getTestFunction(String structurePath, CallbackInfoReturnable> cir) { 26 | if (cir.getReturnValue().isEmpty()) { 27 | String templateName = TestRegistryHelper.getTemplateName(structurePath); 28 | if (templateName == null) { 29 | return; 30 | } 31 | Function> testTemplate = TestRegistryHelper.getTestTemplate(templateName); 32 | if (testTemplate != null) { 33 | Stream apply = testTemplate.apply(structurePath); 34 | if (apply == null) { 35 | return; 36 | } 37 | List list = apply.toList(); 38 | if (list.isEmpty()) { 39 | return; 40 | } 41 | TestConfig testConfig = list.get((int) (list.size() * Math.random())); 42 | cir.setReturnValue(Optional.of(testConfig.toTestFunction())); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/after_test/TestCommandMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.after_test; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import mctester.McTesterConfig; 5 | import net.minecraft.server.command.TestCommand; 6 | import net.minecraft.server.world.ServerWorld; 7 | import net.minecraft.util.Formatting; 8 | import net.minecraft.util.crash.CrashException; 9 | import net.minecraft.util.crash.CrashReport; 10 | import org.slf4j.Logger; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Unique; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | @Mixin(TestCommand.class) 18 | public class TestCommandMixin { 19 | @Unique 20 | private static final Logger LOGGER = LogUtils.getLogger(); 21 | 22 | @Inject(at = @At(value = "HEAD"), method = "sendMessage(Lnet/minecraft/server/world/ServerWorld;Ljava/lang/String;Lnet/minecraft/util/Formatting;)V") 23 | private static void handleTestEnd(ServerWorld world, String message, Formatting formatting, CallbackInfo ci) { 24 | LOGGER.info(message); 25 | if (message.endsWith(" required tests failed :(")) { 26 | if (McTesterConfig.shouldCrashOnFail()) { 27 | intentionallyCrashServerDueToFailedTests(); 28 | } else if (McTesterConfig.shouldShutdownAfterTest() && !McTesterConfig.shouldStayUpAfterFail()) { 29 | world.getServer().stop(false); 30 | } 31 | } else if (message.endsWith("All required tests passed :)")) { 32 | if (McTesterConfig.shouldShutdownAfterTest()) { 33 | world.getServer().stop(false); 34 | } 35 | } 36 | } 37 | 38 | @Unique 39 | private static void intentionallyCrashServerDueToFailedTests() { 40 | throw new CrashException(new CrashReport("Crashing due to failed tests. This can be disabled in the mctester config.", new Throwable())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/startup/Autostart_MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.startup; 2 | 3 | import mctester.McTesterConfig; 4 | import net.minecraft.server.MinecraftServer; 5 | import net.minecraft.server.command.CommandManager; 6 | import net.minecraft.server.command.ServerCommandSource; 7 | import net.minecraft.test.TestFunction; 8 | import net.minecraft.test.TestFunctions; 9 | import org.slf4j.Logger; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | import java.util.Collection; 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Random; 21 | 22 | @Mixin(MinecraftServer.class) 23 | public abstract class Autostart_MinecraftServerMixin { 24 | @Shadow public abstract CommandManager getCommandManager(); 25 | 26 | @Shadow public abstract ServerCommandSource getCommandSource(); 27 | 28 | @Shadow @Final private static Logger LOGGER; 29 | 30 | @Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;createMetadata()Lnet/minecraft/server/ServerMetadata;")) 31 | private void runAllTests(CallbackInfo ci) { 32 | if (McTesterConfig.shouldAutorun()) { 33 | if (McTesterConfig.shouldShuffleBeforeAutorun()) { 34 | Collection testFunctions = TestFunctions.getTestFunctions(); 35 | long seed = McTesterConfig.shuffleSeed(); 36 | Random random = new Random(seed); 37 | LOGGER.info("Shuffling tests with random seed: " + seed); 38 | if (testFunctions instanceof List) { 39 | Collections.shuffle((List) testFunctions, random); 40 | } 41 | } 42 | this.getCommandManager().executeWithPrefix(this.getCommandSource(), "/test runall"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/after_test/crash_exit_code_fix/MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.after_test.crash_exit_code_fix; 2 | 3 | import net.minecraft.server.MinecraftServer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Unique; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.Redirect; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | /** 12 | * This mixin makes the server crashing use System.exit(1) instead of System.exit(0). For example gradle can use this to 13 | * detect the failure. 14 | */ 15 | @Mixin(MinecraftServer.class) 16 | public class MinecraftServerMixin { 17 | @Unique 18 | private boolean systemExit1WithServerThread = false; 19 | 20 | @Inject(method = "runServer()V", 21 | at = @At( 22 | value = "INVOKE", 23 | target = "Lnet/minecraft/server/MinecraftServer;setCrashReport(Lnet/minecraft/util/crash/CrashReport;)V" 24 | ) 25 | ) 26 | private void rememberCrash(CallbackInfo ci) { 27 | this.systemExit1WithServerThread = true; 28 | } 29 | 30 | @Inject(method = "runServer", 31 | at = @At( 32 | value = "INVOKE", 33 | target = "Lnet/minecraft/server/MinecraftServer;exit()V", 34 | shift = At.Shift.AFTER 35 | ) 36 | ) 37 | private void exitWithStatusCode(CallbackInfo ci) { 38 | if (this.systemExit1WithServerThread) { 39 | System.exit(1); 40 | } 41 | } 42 | 43 | @Redirect(method = "stop(Z)V", 44 | at = @At( 45 | value = "INVOKE", 46 | target = "Ljava/lang/Thread;join()V" 47 | ) 48 | ) 49 | private void join(Thread thread) throws InterruptedException { 50 | //a thread that called System.exit() should not be waited for in the shutdown hook 51 | if (!this.systemExit1WithServerThread) { 52 | thread.join(); 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/mctester/tests/Example.java: -------------------------------------------------------------------------------- 1 | package mctester.tests; 2 | 3 | import mctester.common.test.creation.GameTestHelper; 4 | import mctester.common.test.exceptions.GameTestAssertException; 5 | import mctester.common.util.GameTestUtil; 6 | import net.minecraft.block.Blocks; 7 | import net.minecraft.entity.EntityType; 8 | import net.minecraft.entity.passive.CowEntity; 9 | import net.minecraft.nbt.NbtCompound; 10 | import net.minecraft.registry.tag.FluidTags; 11 | import net.minecraft.test.GameTest; 12 | import net.minecraft.test.PositionedException; 13 | import net.minecraft.test.TestContext; 14 | import net.minecraft.util.math.BlockPos; 15 | 16 | public class Example { 17 | public static final NbtCompound PERSISTENCE_REQUIRED = new NbtCompound(); 18 | 19 | static { 20 | PERSISTENCE_REQUIRED.putBoolean("PersistenceRequired", true); 21 | } 22 | 23 | @GameTest 24 | public static void cow_on_cactus(TestContext context) { 25 | GameTestHelper helper = GameTestHelper.get(context); 26 | CowEntity cowEntity = helper.spawnEntity(2, 4, 2, EntityType.COW, PERSISTENCE_REQUIRED); 27 | helper.succeedWhen( 28 | helper1 -> !cowEntity.isAlive(), 29 | helper1 -> { 30 | BlockPos relativePos = new BlockPos(2, 4, 2); 31 | return new PositionedException("Expected dead cow", GameTestUtil.transformRelativeToAbsolutePos(helper1.gameTest, relativePos), relativePos, helper1.currTick); 32 | } 33 | ); 34 | } 35 | 36 | @GameTest( 37 | rotation = 5, 38 | duration = 0 //set a cooldown to allow firing observers etc. to stop before starting the test. We don't need that for waterflow. 39 | ) 40 | public static void waterflow1(TestContext context) { 41 | GameTestHelper helper = GameTestHelper.get(context); 42 | helper.setBlockState(4, 3, 2, Blocks.WATER.getDefaultState()); 43 | helper.succeedWhen( 44 | helper1 -> helper.getBlockState(2, 2, 2).getFluidState().isIn(FluidTags.WATER), 45 | helper1 -> { 46 | BlockPos relativePos = new BlockPos(2, 2, 2); 47 | return new PositionedException("Expected water", GameTestUtil.transformRelativeToAbsolutePos(helper1.gameTest, relativePos), relativePos, helper1.currTick); 48 | } 49 | ); 50 | } 51 | 52 | @GameTest(tickLimit = 300) 53 | public static void wolf_skeleton_fight(TestContext context) { 54 | GameTestHelper helper = GameTestHelper.get(context); 55 | helper.spawnEntity(2, 2, 2, EntityType.SKELETON, PERSISTENCE_REQUIRED); 56 | helper.spawnEntity(2, 2, 2, EntityType.WOLF, PERSISTENCE_REQUIRED); 57 | 58 | helper.succeedWhen( 59 | helper1 -> helper1.getEntitiesInside().stream().anyMatch( 60 | entity -> (entity.getType() == EntityType.SKELETON || entity.getType() == EntityType.WOLF) 61 | && !entity.isAlive()), 62 | helper1 -> new GameTestAssertException("Expected dead skeleton or dead wolf!") 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MC Tester Mod 2 | 3 | This mod allows using the automated structure based [test system](https://www.youtube.com/watch?v=vXaWOJTCYNg) Mojang 4 | created for minecraft. The test system is only partially included in minecraft and this mod adds some missing parts to 5 | make it work. Furthermore, more features, for example autorun and non-0 exit-code on failure, are included to make the 6 | usage more convenient. The tests can be run with gradle or even with github actions see the 7 | repositories' [build workflow file](https://github.com/2No2Name/McTester/blob/master/.github/workflows/build.yml). 8 | 9 | ## Usage as a dependency 10 | 11 | Add the following to build.gradle (use existing dependencies/maven blocks) or put a build from the 12 | [releases page](https://github.com/2No2Name/McTester/releases) into the `mods` folder. Currently the mctester mod will 13 | be loaded at every startup e.g. `gradlew runClient` or `gradlew runServer`. 14 | 15 | ``` 16 | dependencies { 17 | modImplementation 'com.github.2No2Name:McTester:VERSION_TAG_HERE' 18 | } 19 | 20 | repositories { 21 | allprojects { 22 | repositories { 23 | maven { url 'https://jitpack.io' } 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | Edit the config/mctester.properties file to adjust available options. Put test structures into the gameteststructures 30 | directory. Prefixing the structure names with a template name, e.g. 31 | `test_redstone.` will automatically use the test function from the template. Other test functions can be defined by 32 | using the [@Test](https://github.com/2No2Name/McTester/blob/master/src/main/java/mctester/annotation/Test.java) 33 | annotation, 34 | cf. [Example test functions](https://github.com/2No2Name/McTester/blob/master/src/main/java/mctester/ExampleTests.java). 35 | 36 | ## Available test templates 37 | 38 | See the test [templates file](https://github.com/2No2Name/McTester/blob/master/src/main/java/mctester/Templates.java) 39 | 40 | - `test_redstone.`: Replaces all red stained terracotta with redstone blocks in the structure's area (test area) 41 | when the test is activated. Automatically fails after 20 seconds. Test succeeds if there is a powered noteblock on top 42 | of an emerald block or green/lime wool in the test area. The success block positions are cached at the start of the 43 | test, so placing or removing more success condition blocks may break the test success detection. The test can also be 44 | failed by powering a noteblock on top of a red wool. The success/failure conditions are checked once per tick, with 45 | the test failing when both success and failure are detected in the same tick. 46 | 47 | ## Available options 48 | 49 | The following options can changed in the mctester.properties config text file. You may need to create the file manually. 50 | 51 | Format: `option=VALUE`, possible values: `true`, `serveronly`, `clientonly`, `false` 52 | Defaults: 53 | 54 | - `autostart=false` Automatically runs all tests when a world is loaded. 55 | - `autostart.shuffle=true` Shuffles the tests before automatically running them. 56 | - `autostart.shuffle.seed` Set the shuffle seed for debug purposes. When this option is not defined, a random seed is 57 | used. 58 | - `crashOnFail=false` Automatically crashes the server when any test fails. 59 | - `shutdownAfterTest=false` Automatically shut down the server after the tests finished. 60 | - `stayUpAfterFail=false` Do not shutdown the server if a test failed. 61 | - `isDevelopment=false` Sets Minecraft's SharedConstants.isDevelopment field if true. 62 | - `includeExampleTests=false` Loads example test functions defined by this mod. Templates are always loaded. 63 | 64 | ## Setup for development 65 | 66 | For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the 67 | IDE that you are using. 68 | -------------------------------------------------------------------------------- /src/main/java/mctester/annotation/TestRegistryHelper.java: -------------------------------------------------------------------------------- 1 | package mctester.annotation; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; 5 | import mctester.common.test.creation.TestConfig; 6 | import net.minecraft.test.*; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.slf4j.Logger; 9 | 10 | import java.io.File; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import java.util.function.Function; 16 | import java.util.stream.Stream; 17 | 18 | public class TestRegistryHelper { 19 | private static final Logger LOGGER = LogUtils.getLogger(); 20 | public static boolean shouldWarnOnMissingStructureFile = true; 21 | public static final Object2ReferenceOpenHashMap>> TEST_TEMPLATES = new Object2ReferenceOpenHashMap<>(); 22 | 23 | public static void createTestTemplateFromClass(Class clazz) { 24 | Arrays.stream(clazz.getDeclaredMethods()) 25 | .filter(m -> m.getAnnotation(GameTestTemplate.class) != null) 26 | .forEach(TestRegistryHelper::createTestTemplateFromMethod); 27 | } 28 | 29 | public static void createTestsFromClass(Class clazz) { 30 | TestFunctions.register(clazz); 31 | } 32 | 33 | public static void createTemplatedTestsFromFiles() { 34 | String structuresDirectoryName = StructureTestUtil.testStructuresDirectoryName; 35 | File[] files = new File(structuresDirectoryName).listFiles(); 36 | if (files != null) { 37 | for (File file : files) { 38 | String fileName = file.getName(); 39 | if (!file.isFile() || !file.canRead() || !fileName.endsWith(".snbt")) continue; 40 | String structureName = fileName.substring(0, fileName.length() - ".snbt".length()); 41 | String templateName = getTemplateName(structureName); 42 | if (templateName == null) continue; 43 | Function> testInitializer = getTestTemplate(templateName); 44 | if (testInitializer != null) { 45 | createTestsForTemplatedStructure(structureName, templateName, testInitializer); 46 | } 47 | } 48 | } 49 | } 50 | 51 | public static Function> getTestTemplate(String templateName) { 52 | return TEST_TEMPLATES.get(templateName); 53 | } 54 | 55 | @Nullable 56 | public static String getTemplateName(String structureName) { 57 | int dotIndex = structureName.indexOf("."); 58 | if (dotIndex < 0) { 59 | return null; 60 | } 61 | return structureName.substring(0, dotIndex); 62 | } 63 | 64 | @SuppressWarnings("unchecked") 65 | public static void createTestTemplateFromMethod(Method method) { 66 | GameTestTemplate annotation = method.getAnnotation(GameTestTemplate.class); 67 | TEST_TEMPLATES.put(annotation.name(), s -> { //Should prob just pass the method 68 | try { 69 | return (Stream) method.invoke(null, s); 70 | } catch (IllegalAccessException | InvocationTargetException e) { 71 | LOGGER.warn("Unable to load test template: " + annotation.name()); 72 | } 73 | return null; 74 | }); 75 | } 76 | 77 | public static void createTestsForTemplatedStructure(String structureName, String templateName, Function> testInitializer) { 78 | List apply = testInitializer.apply(structureName).toList(); 79 | for (TestConfig testConfig : apply) { 80 | TestFunction testFunction = testConfig.toTestFunction(); 81 | registerTest(testFunction, templateName); 82 | } 83 | } 84 | 85 | public static void registerTest(TestFunction testFunction, String className) { 86 | TestFunctions.getTestFunctions().add(testFunction); 87 | if (!className.isEmpty()) { 88 | TestFunctions.getTestClasses().add(className); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/mctester/mixin/variants/TestFunctionsMixin.java: -------------------------------------------------------------------------------- 1 | package mctester.mixin.variants; 2 | 3 | 4 | import mctester.annotation.GameTestExtra; 5 | import mctester.common.test.creation.TestConfig; 6 | import mctester.common.util.BlockRotationUtil; 7 | import net.minecraft.test.GameTest; 8 | import net.minecraft.test.StructureTestUtil; 9 | import net.minecraft.test.TestFunction; 10 | import net.minecraft.test.TestFunctions; 11 | import net.minecraft.util.BlockRotation; 12 | import org.spongepowered.asm.mixin.Final; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.Unique; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Redirect; 18 | 19 | import java.lang.reflect.Method; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import java.util.Optional; 23 | import java.util.Random; 24 | import java.util.stream.Stream; 25 | 26 | 27 | @Mixin(TestFunctions.class) 28 | public abstract class TestFunctionsMixin { 29 | 30 | @Shadow @Final private static Collection TEST_FUNCTIONS; 31 | 32 | @Shadow 33 | private static TestFunction getTestFunction(Method method) { 34 | throw new AssertionError(); 35 | } 36 | 37 | @Redirect( 38 | method = "register(Ljava/lang/reflect/Method;)V", 39 | at = @At(value = "INVOKE", target = "Lnet/minecraft/test/TestFunctions;getTestFunction(Ljava/lang/reflect/Method;)Lnet/minecraft/test/TestFunction;", ordinal = 0) 40 | ) 41 | private static TestFunction registerVariants(Method method) { 42 | GameTestExtra gameTestExtra = method.getAnnotation(GameTestExtra.class); 43 | int numVariants = gameTestExtra == null ? 1 : gameTestExtra.variants(); 44 | 45 | GameTest gameTest = method.getAnnotation(GameTest.class); 46 | if (numVariants != 1 || BlockRotationUtil.isCustom(gameTest.rotation())) { 47 | registerVariants(gameTest, method, numVariants); 48 | return null; 49 | } 50 | return getTestFunction(method); 51 | } 52 | 53 | @Unique 54 | private static void registerVariants(GameTest gameTest, Method method, int variants) { 55 | BlockRotation[] rotations = BlockRotationUtil.getRotations(gameTest.rotation()); 56 | for (BlockRotation rotation : rotations) { 57 | for (int i = 0; i < variants; i++) { 58 | TestFunction testFunction = getTestFunction(method); 59 | testFunction = TestConfig.from(testFunction).rotation(rotation).variant(i).toTestFunction(); 60 | TEST_FUNCTIONS.add(testFunction); 61 | } 62 | } 63 | } 64 | 65 | @Redirect( 66 | method = "register(Ljava/lang/reflect/Method;)V", 67 | at = @At(value = "INVOKE", target = "Ljava/util/Collection;add(Ljava/lang/Object;)Z", ordinal = 0) 68 | ) 69 | private static boolean skipNull(Collection instance, E e) { 70 | if (e != null) { 71 | return instance.add(e); 72 | } 73 | return false; 74 | } 75 | 76 | @Redirect( 77 | method = "getTestFunction(Ljava/lang/reflect/Method;)Lnet/minecraft/test/TestFunction;", 78 | at = @At(value = "INVOKE", target = "Lnet/minecraft/test/StructureTestUtil;getRotation(I)Lnet/minecraft/util/BlockRotation;", ordinal = 0) 79 | ) 80 | private static BlockRotation handleOutOfRange(int steps) { 81 | if (BlockRotationUtil.isCustom(steps)) { 82 | return BlockRotation.NONE; 83 | } 84 | return StructureTestUtil.getRotation(steps); 85 | } 86 | 87 | 88 | //Todo alternatively / better: Store the variant of the test function in the structure block 89 | @Redirect( 90 | method = "getTestFunction(Ljava/lang/String;)Ljava/util/Optional;", 91 | at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;findFirst()Ljava/util/Optional;") 92 | ) 93 | private static Optional findRandom(Stream instance) { 94 | List list = instance.toList(); 95 | if (list.isEmpty()) { 96 | return Optional.empty(); 97 | } 98 | Random random = new Random(); 99 | return Optional.of(list.get(random.nextInt(list.size()))); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/test/creation/TestConfig.java: -------------------------------------------------------------------------------- 1 | package mctester.common.test.creation; 2 | 3 | import mctester.mixin.accessor.TestContextAccessor; 4 | import mctester.common.util.TestFunctionWithVariant; 5 | import net.minecraft.test.TestContext; 6 | import net.minecraft.test.TestFunction; 7 | import net.minecraft.util.BlockRotation; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.function.Consumer; 11 | 12 | public class TestConfig { 13 | 14 | private String batchId = "defaultbatch"; 15 | private String structurePath; 16 | private String structureName; 17 | private long cooldown = 0; //time between loading the structure and starting the test 18 | private int timeout = 400; //time after which the test automatically fails 19 | private boolean required = true; 20 | private BlockRotation rotation = BlockRotation.NONE; 21 | private int maxAttempts; 22 | private int requiredSuccesses; 23 | 24 | private Consumer starter; 25 | 26 | private int variant; 27 | private boolean manualOnly; 28 | private boolean skyAccess; 29 | 30 | 31 | public TestConfig(@NotNull Consumer starter) { 32 | this.starter = startupParameter -> starter.accept( 33 | ((GameTestHelper.GameTestAccess) ((TestContextAccessor) startupParameter).getTest()).mcTester$getGameTestHelper()); 34 | } 35 | private TestConfig() {} 36 | 37 | public static TestConfig from(TestFunction testFunction) { 38 | TestConfig testConfig = new TestConfig(); 39 | testConfig.batchId = testFunction.batchId(); 40 | testConfig.structurePath = testFunction.templatePath(); 41 | testConfig.structureName = testFunction.templateName(); 42 | testConfig.rotation = testFunction.rotation(); 43 | testConfig.timeout = testFunction.tickLimit(); 44 | testConfig.cooldown = testFunction.setupTicks(); 45 | testConfig.required = testFunction.required(); 46 | testConfig.manualOnly = testFunction.manualOnly(); 47 | testConfig.maxAttempts = testFunction.maxAttempts(); 48 | testConfig.requiredSuccesses = testFunction.requiredSuccesses(); 49 | testConfig.skyAccess = testFunction.skyAccess(); 50 | testConfig.starter = testFunction.starter(); 51 | return testConfig; 52 | } 53 | 54 | public TestFunction toTestFunction() { 55 | TestFunction testFunction = new TestFunction(this.batchId, this.structurePath, this.structureName, this.rotation, this.timeout, this.cooldown, this.required, this.manualOnly, this.maxAttempts, this.requiredSuccesses, this.skyAccess , this.starter); 56 | ((TestFunctionWithVariant) (Object) testFunction).mcTester$setVariant(this.variant); 57 | return testFunction; 58 | } 59 | 60 | public TestConfig batchId(String batchId) { 61 | this.batchId = batchId; 62 | return this; 63 | } 64 | 65 | public TestConfig structureName(String structurePath) { 66 | this.structurePath = structurePath; 67 | this.structureName = structurePath; 68 | return this; 69 | } 70 | 71 | public TestConfig required(boolean required) { 72 | this.required = required; 73 | return this; 74 | } 75 | 76 | public TestConfig structurePlaceCooldown(long ticks) { 77 | this.cooldown = ticks; 78 | return this; 79 | } 80 | 81 | public TestConfig timeout(int ticks) { 82 | this.timeout = ticks; 83 | return this; 84 | } 85 | 86 | public TestConfig rotation(BlockRotation rotation) { 87 | this.rotation = rotation; 88 | return this; 89 | } 90 | 91 | public TestConfig requiredSuccessCount(int requiredSuccessCount) { 92 | this.requiredSuccesses = requiredSuccessCount; 93 | return this; 94 | } 95 | 96 | public TestConfig repetitions(int repetitions) { 97 | this.maxAttempts = repetitions; 98 | return this; 99 | } 100 | 101 | public TestConfig variant(int variant) { 102 | this.variant = variant; 103 | return this; 104 | } 105 | 106 | public TestConfig manualOnly(boolean manualOnly) { 107 | this.manualOnly = manualOnly; 108 | return this; 109 | } 110 | 111 | public TestConfig skyAccess(boolean skyAccess) { 112 | this.skyAccess = skyAccess; 113 | return this; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/mctester/templates/TestRedstoneTemplate.java: -------------------------------------------------------------------------------- 1 | package mctester.templates; 2 | 3 | import mctester.annotation.GameTestTemplate; 4 | import mctester.common.test.creation.GameTestHelper; 5 | import mctester.common.test.creation.TestConfig; 6 | import mctester.common.test.exceptions.GameTestAssertException; 7 | import mctester.common.util.GameTestUtil; 8 | import net.minecraft.block.BlockState; 9 | import net.minecraft.block.Blocks; 10 | import net.minecraft.block.NoteBlock; 11 | import net.minecraft.structure.StructureTemplate; 12 | import net.minecraft.test.PositionedException; 13 | import net.minecraft.util.BlockMirror; 14 | import net.minecraft.util.math.BlockPos; 15 | 16 | import java.util.ArrayList; 17 | import java.util.stream.Stream; 18 | 19 | public class TestRedstoneTemplate { 20 | 21 | @GameTestTemplate(name = "test_redstone") 22 | public static Stream testFromStructure(String structureName) { 23 | TestConfig testConfig = new TestConfig(TestRedstoneTemplate::test_redstone).structureName(structureName); 24 | //10 tick delay at the start to avoid accidental success condition activation due to redstone flickering 25 | testConfig = testConfig.structurePlaceCooldown(10); 26 | return Stream.of(testConfig); 27 | } 28 | 29 | private static boolean isSuccessBlock(BlockState blockState) { 30 | return blockState.isOf(Blocks.EMERALD_BLOCK) || blockState.isOf(Blocks.GREEN_WOOL) || blockState.isOf(Blocks.LIME_WOOL); 31 | } 32 | 33 | private static boolean isFailureBlock(BlockState blockState) { 34 | return blockState.isOf(Blocks.RED_WOOL); 35 | } 36 | 37 | /** 38 | * A test function that can be used to create tests with a simple redstone interface. 39 | */ 40 | public static void test_redstone(GameTestHelper helper) { 41 | ArrayList successBlocks = new ArrayList<>(); 42 | ArrayList failureBlocks = new ArrayList<>(); 43 | 44 | //Replace all red terracotta with redstone block at the start and fill the condition block lists 45 | GameTestUtil.streamPositions(helper.gameTest).forEach(blockPos -> { 46 | BlockState blockState = helper.gameTest.getWorld().getBlockState(blockPos); 47 | if (blockState.isOf(Blocks.RED_TERRACOTTA)) { 48 | helper.gameTest.getWorld().setBlockState(blockPos, Blocks.REDSTONE_BLOCK.getDefaultState()); 49 | } 50 | if (isSuccessBlock(blockState)) successBlocks.add(blockPos.toImmutable()); 51 | if (isFailureBlock(blockState)) failureBlocks.add(blockPos.toImmutable()); 52 | }); 53 | if (successBlocks.isEmpty()) { 54 | throw new GameTestAssertException("Expected success condition blocks anywhere inside the test. test_redstone requires green or lime wool or emerald blocks for the success condition"); 55 | } 56 | 57 | //Succeed when any powered note block is on top of a success condition block. Assume the success condition block doesn't move etc. 58 | helper.succeedWhen( 59 | helper1 -> { 60 | //Always check the failure condition blocks before the success conditions. Failed tests throw exceptions. 61 | if ( 62 | failureBlocks.stream().anyMatch(blockPos -> { 63 | BlockState blockState = helper1.gameTest.getWorld().getBlockState(blockPos.up()); 64 | return blockState.isOf(Blocks.NOTE_BLOCK) && blockState.get(NoteBlock.POWERED) && 65 | isFailureBlock(helper1.gameTest.getWorld().getBlockState(blockPos)); 66 | })) { 67 | BlockPos blockPos = helper1.gameTest.getPos(); 68 | BlockPos absolutePos = successBlocks.get(0); 69 | BlockPos relativePos = StructureTemplate.transformAround(absolutePos, BlockMirror.NONE, GameTestUtil.getInverse(helper1.gameTest.getRotation()), blockPos).add(-blockPos.getX(), -blockPos.getY(), -blockPos.getZ()); 70 | throw new PositionedException("Failure condition was met with powered noteblock on top of a failure condition block", absolutePos, relativePos, helper1.currTick); 71 | } 72 | return successBlocks.stream().anyMatch(blockPos -> { 73 | BlockState blockState = helper1.gameTest.getWorld().getBlockState(blockPos.up()); 74 | return blockState.isOf(Blocks.NOTE_BLOCK) && blockState.get(NoteBlock.POWERED) && 75 | isSuccessBlock(helper1.gameTest.getWorld().getBlockState(blockPos)); 76 | }); 77 | }, 78 | helper1 -> { 79 | BlockPos blockPos = helper1.gameTest.getPos(); 80 | BlockPos absolutePos = successBlocks.get(0); 81 | BlockPos relativePos = StructureTemplate.transformAround(absolutePos, BlockMirror.NONE, GameTestUtil.getInverse(helper1.gameTest.getRotation()), blockPos).add(-blockPos.getX(), -blockPos.getY(), -blockPos.getZ()); 82 | return new PositionedException("Expected powered noteblock on top of an success condition block. For example", absolutePos, relativePos, helper1.currTick); 83 | } 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/mctester/McTesterConfig.java: -------------------------------------------------------------------------------- 1 | package mctester; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.loader.api.FabricLoader; 6 | import org.slf4j.Logger; 7 | 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.Properties; 13 | import java.util.Random; 14 | 15 | public class McTesterConfig { 16 | private static final Logger LOGGER = LogUtils.getLogger(); 17 | private static boolean shouldAutorun; 18 | //shuffling the test functions leads to the same test not always running in the same position and with the same neighboring tests 19 | //if a test breaks due to interaction with a neighboring tests, it or the neighboring test should be redesigned 20 | private static boolean shouldAutorunShuffle; 21 | private static boolean shouldCrashOnFail; 22 | private static boolean shouldShutdownAfterTest; 23 | private static boolean shouldStayUpAfterFail; 24 | private static long autorunShuffleSeed; 25 | private static boolean isDevelopment; 26 | private static boolean includeExampleTests; 27 | private static boolean includeExampleTemplates; 28 | 29 | static { 30 | LOGGER.info("Loading default config..."); 31 | isDevelopment = false; 32 | shouldAutorun = true; 33 | shouldAutorunShuffle = true; 34 | autorunShuffleSeed = new Random().nextLong(); 35 | shouldCrashOnFail = FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER; 36 | shouldShutdownAfterTest = FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER; 37 | shouldStayUpAfterFail = false; 38 | includeExampleTests = true; 39 | includeExampleTemplates = true; 40 | 41 | ArrayList optionValues = new ArrayList<>(); 42 | optionValues.add("false"); 43 | optionValues.add("true"); 44 | optionValues.add("serveronly"); 45 | optionValues.add("clientonly"); 46 | boolean[] optionValueLookup = { 47 | false, 48 | true, 49 | FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER, 50 | FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT 51 | }; 52 | 53 | Properties properties = new Properties(); 54 | File file = new File("./config/mctester.properties"); 55 | if (file.exists()) { 56 | try (FileInputStream in = new FileInputStream(file)) { 57 | LOGGER.info("Found configuration file, loading properties!"); 58 | properties.load(in); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | LOGGER.error("Error closing config file!"); 62 | } 63 | 64 | String autostartProperty = properties.getProperty("autostart"); 65 | if (autostartProperty != null) { 66 | shouldAutorun = optionValueLookup[Math.max(0, optionValues.indexOf(autostartProperty))]; 67 | } 68 | String autostartShuffleProperty = properties.getProperty("autostart.shuffle"); 69 | if (autostartShuffleProperty != null) { 70 | shouldAutorunShuffle = optionValueLookup[Math.max(0, optionValues.indexOf(autostartShuffleProperty))]; 71 | } 72 | String autostartShuffleSeedProperty = properties.getProperty("autostart.shuffle.seed"); 73 | if (autostartShuffleSeedProperty != null) { 74 | autorunShuffleSeed = Long.parseLong(autostartShuffleSeedProperty); 75 | } 76 | String crashOnFailProperty = properties.getProperty("crashOnFail"); 77 | if (crashOnFailProperty != null) { 78 | shouldCrashOnFail = optionValueLookup[Math.max(0, optionValues.indexOf(crashOnFailProperty))]; 79 | } 80 | String shutdownAfterTestProperty = properties.getProperty("shutdownAfterTest"); 81 | if (shutdownAfterTestProperty != null) { 82 | shouldShutdownAfterTest = optionValueLookup[Math.max(0, optionValues.indexOf(shutdownAfterTestProperty))]; 83 | } 84 | String stayUpAfterFailProperty = properties.getProperty("stayUpAfterFail"); 85 | if (stayUpAfterFailProperty != null) { 86 | shouldStayUpAfterFail = optionValueLookup[Math.max(0, optionValues.indexOf(stayUpAfterFailProperty))]; 87 | } 88 | String isDevelopmentProperty = properties.getProperty("isDevelopment"); 89 | if (isDevelopmentProperty != null) { 90 | isDevelopment = optionValueLookup[Math.max(0, optionValues.indexOf(isDevelopmentProperty))]; 91 | } 92 | String includeExampleTestsProperty = properties.getProperty("includeExampleTests"); 93 | if (includeExampleTestsProperty != null) { 94 | includeExampleTests = optionValueLookup[Math.max(0, optionValues.indexOf(includeExampleTestsProperty))]; 95 | } 96 | String includeExampleTemplatesProperty = properties.getProperty("includeExampleTemplates"); 97 | if (includeExampleTemplatesProperty != null) { 98 | includeExampleTemplates = optionValueLookup[Math.max(0, optionValues.indexOf(includeExampleTemplatesProperty))]; 99 | } 100 | } 101 | } 102 | 103 | public static boolean shouldAutorun() { 104 | return shouldAutorun; 105 | } 106 | 107 | public static boolean shouldShuffleBeforeAutorun() { 108 | return shouldAutorunShuffle; 109 | } 110 | 111 | public static long shuffleSeed() { 112 | return autorunShuffleSeed; 113 | } 114 | 115 | public static boolean shouldCrashOnFail() { 116 | return shouldCrashOnFail; 117 | } 118 | 119 | public static boolean shouldShutdownAfterTest() { 120 | return shouldShutdownAfterTest; 121 | } 122 | 123 | public static boolean shouldStayUpAfterFail() { 124 | return shouldStayUpAfterFail; 125 | } 126 | 127 | public static boolean isDevelopment() { 128 | return isDevelopment; 129 | } 130 | 131 | public static boolean shouldIncludeExampleTests() { 132 | return includeExampleTests; 133 | } 134 | 135 | public static boolean shouldIncludeExampleTemplates() { 136 | return includeExampleTemplates; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /run/gameteststructures/example.cow_on_cactus.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:glass"}, 31 | {pos: [0, 1, 1], state: "minecraft:glass"}, 32 | {pos: [0, 1, 2], state: "minecraft:glass"}, 33 | {pos: [0, 1, 3], state: "minecraft:glass"}, 34 | {pos: [0, 1, 4], state: "minecraft:glass"}, 35 | {pos: [1, 1, 0], state: "minecraft:glass"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:glass"}, 40 | {pos: [2, 1, 0], state: "minecraft:glass"}, 41 | {pos: [2, 1, 1], state: "minecraft:air"}, 42 | {pos: [2, 1, 2], state: "minecraft:sand"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:glass"}, 45 | {pos: [3, 1, 0], state: "minecraft:glass"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:glass"}, 50 | {pos: [4, 1, 0], state: "minecraft:glass"}, 51 | {pos: [4, 1, 1], state: "minecraft:glass"}, 52 | {pos: [4, 1, 2], state: "minecraft:glass"}, 53 | {pos: [4, 1, 3], state: "minecraft:glass"}, 54 | {pos: [4, 1, 4], state: "minecraft:glass"}, 55 | {pos: [0, 2, 0], state: "minecraft:glass"}, 56 | {pos: [0, 2, 1], state: "minecraft:glass"}, 57 | {pos: [0, 2, 2], state: "minecraft:glass"}, 58 | {pos: [0, 2, 3], state: "minecraft:glass"}, 59 | {pos: [0, 2, 4], state: "minecraft:glass"}, 60 | {pos: [1, 2, 0], state: "minecraft:glass"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:glass"}, 65 | {pos: [2, 2, 0], state: "minecraft:glass"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:cactus{age:2}"}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:glass"}, 70 | {pos: [3, 2, 0], state: "minecraft:glass"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:glass"}, 75 | {pos: [4, 2, 0], state: "minecraft:glass"}, 76 | {pos: [4, 2, 1], state: "minecraft:glass"}, 77 | {pos: [4, 2, 2], state: "minecraft:glass"}, 78 | {pos: [4, 2, 3], state: "minecraft:glass"}, 79 | {pos: [4, 2, 4], state: "minecraft:glass"}, 80 | {pos: [0, 3, 0], state: "minecraft:glass"}, 81 | {pos: [0, 3, 1], state: "minecraft:glass"}, 82 | {pos: [0, 3, 2], state: "minecraft:glass"}, 83 | {pos: [0, 3, 3], state: "minecraft:glass"}, 84 | {pos: [0, 3, 4], state: "minecraft:glass"}, 85 | {pos: [1, 3, 0], state: "minecraft:glass"}, 86 | {pos: [1, 3, 1], state: "minecraft:glass"}, 87 | {pos: [1, 3, 2], state: "minecraft:glass"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:glass"}, 90 | {pos: [2, 3, 0], state: "minecraft:glass"}, 91 | {pos: [2, 3, 1], state: "minecraft:glass"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:glass"}, 94 | {pos: [2, 3, 4], state: "minecraft:glass"}, 95 | {pos: [3, 3, 0], state: "minecraft:glass"}, 96 | {pos: [3, 3, 1], state: "minecraft:glass"}, 97 | {pos: [3, 3, 2], state: "minecraft:glass"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:glass"}, 100 | {pos: [4, 3, 0], state: "minecraft:glass"}, 101 | {pos: [4, 3, 1], state: "minecraft:glass"}, 102 | {pos: [4, 3, 2], state: "minecraft:glass"}, 103 | {pos: [4, 3, 3], state: "minecraft:glass"}, 104 | {pos: [4, 3, 4], state: "minecraft:glass"}, 105 | {pos: [0, 4, 0], state: "minecraft:glass"}, 106 | {pos: [0, 4, 1], state: "minecraft:glass"}, 107 | {pos: [0, 4, 2], state: "minecraft:glass"}, 108 | {pos: [0, 4, 3], state: "minecraft:glass"}, 109 | {pos: [0, 4, 4], state: "minecraft:glass"}, 110 | {pos: [1, 4, 0], state: "minecraft:glass"}, 111 | {pos: [1, 4, 1], state: "minecraft:glass"}, 112 | {pos: [1, 4, 2], state: "minecraft:glass"}, 113 | {pos: [1, 4, 3], state: "minecraft:glass"}, 114 | {pos: [1, 4, 4], state: "minecraft:glass"}, 115 | {pos: [2, 4, 0], state: "minecraft:glass"}, 116 | {pos: [2, 4, 1], state: "minecraft:glass"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:glass"}, 119 | {pos: [2, 4, 4], state: "minecraft:glass"}, 120 | {pos: [3, 4, 0], state: "minecraft:glass"}, 121 | {pos: [3, 4, 1], state: "minecraft:glass"}, 122 | {pos: [3, 4, 2], state: "minecraft:glass"}, 123 | {pos: [3, 4, 3], state: "minecraft:glass"}, 124 | {pos: [3, 4, 4], state: "minecraft:glass"}, 125 | {pos: [4, 4, 0], state: "minecraft:glass"}, 126 | {pos: [4, 4, 1], state: "minecraft:glass"}, 127 | {pos: [4, 4, 2], state: "minecraft:glass"}, 128 | {pos: [4, 4, 3], state: "minecraft:glass"}, 129 | {pos: [4, 4, 4], state: "minecraft:glass"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:glass", 135 | "minecraft:sand", 136 | "minecraft:air", 137 | "minecraft:cactus{age:2}" 138 | ] 139 | } 140 | -------------------------------------------------------------------------------- /run/gameteststructures/boxfill.fillall.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2703, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:polished_andesite"}, 31 | {pos: [0, 1, 1], state: "minecraft:air"}, 32 | {pos: [0, 1, 2], state: "minecraft:air"}, 33 | {pos: [0, 1, 3], state: "minecraft:air"}, 34 | {pos: [0, 1, 4], state: "minecraft:polished_andesite"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:air"}, 40 | {pos: [2, 1, 0], state: "minecraft:air"}, 41 | {pos: [2, 1, 1], state: "minecraft:air"}, 42 | {pos: [2, 1, 2], state: "minecraft:air"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:air"}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:polished_andesite"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:polished_andesite"}, 55 | {pos: [0, 2, 0], state: "minecraft:polished_andesite"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:air"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:polished_andesite"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:air"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:air"}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:polished_andesite"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:polished_andesite"}, 80 | {pos: [0, 3, 0], state: "minecraft:polished_andesite"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:polished_andesite"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:polished_andesite"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:polished_andesite"}, 105 | {pos: [0, 4, 0], state: "minecraft:polished_andesite"}, 106 | {pos: [0, 4, 1], state: "minecraft:polished_andesite"}, 107 | {pos: [0, 4, 2], state: "minecraft:polished_andesite"}, 108 | {pos: [0, 4, 3], state: "minecraft:polished_andesite"}, 109 | {pos: [0, 4, 4], state: "minecraft:polished_andesite"}, 110 | {pos: [1, 4, 0], state: "minecraft:polished_andesite"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:polished_andesite"}, 115 | {pos: [2, 4, 0], state: "minecraft:polished_andesite"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:polished_andesite"}, 120 | {pos: [3, 4, 0], state: "minecraft:polished_andesite"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:polished_andesite"}, 125 | {pos: [4, 4, 0], state: "minecraft:polished_andesite"}, 126 | {pos: [4, 4, 1], state: "minecraft:polished_andesite"}, 127 | {pos: [4, 4, 2], state: "minecraft:polished_andesite"}, 128 | {pos: [4, 4, 3], state: "minecraft:polished_andesite"}, 129 | {pos: [4, 4, 4], state: "minecraft:polished_andesite"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:air" 135 | ] 136 | } 137 | -------------------------------------------------------------------------------- /run/gameteststructures/example.wolf_skeleton_fight.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:glass"}, 31 | {pos: [0, 1, 1], state: "minecraft:glass"}, 32 | {pos: [0, 1, 2], state: "minecraft:glass"}, 33 | {pos: [0, 1, 3], state: "minecraft:glass"}, 34 | {pos: [0, 1, 4], state: "minecraft:glass"}, 35 | {pos: [1, 1, 0], state: "minecraft:glass"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:glass"}, 40 | {pos: [2, 1, 0], state: "minecraft:glass"}, 41 | {pos: [2, 1, 1], state: "minecraft:air"}, 42 | {pos: [2, 1, 2], state: "minecraft:air"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:glass"}, 45 | {pos: [3, 1, 0], state: "minecraft:glass"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:glass"}, 50 | {pos: [4, 1, 0], state: "minecraft:glass"}, 51 | {pos: [4, 1, 1], state: "minecraft:glass"}, 52 | {pos: [4, 1, 2], state: "minecraft:glass"}, 53 | {pos: [4, 1, 3], state: "minecraft:glass"}, 54 | {pos: [4, 1, 4], state: "minecraft:glass"}, 55 | {pos: [0, 2, 0], state: "minecraft:glass"}, 56 | {pos: [0, 2, 1], state: "minecraft:glass"}, 57 | {pos: [0, 2, 2], state: "minecraft:glass"}, 58 | {pos: [0, 2, 3], state: "minecraft:glass"}, 59 | {pos: [0, 2, 4], state: "minecraft:glass"}, 60 | {pos: [1, 2, 0], state: "minecraft:glass"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:glass"}, 65 | {pos: [2, 2, 0], state: "minecraft:glass"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:air"}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:glass"}, 70 | {pos: [3, 2, 0], state: "minecraft:glass"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:glass"}, 75 | {pos: [4, 2, 0], state: "minecraft:glass"}, 76 | {pos: [4, 2, 1], state: "minecraft:glass"}, 77 | {pos: [4, 2, 2], state: "minecraft:glass"}, 78 | {pos: [4, 2, 3], state: "minecraft:glass"}, 79 | {pos: [4, 2, 4], state: "minecraft:glass"}, 80 | {pos: [0, 3, 0], state: "minecraft:glass"}, 81 | {pos: [0, 3, 1], state: "minecraft:glass"}, 82 | {pos: [0, 3, 2], state: "minecraft:glass"}, 83 | {pos: [0, 3, 3], state: "minecraft:glass"}, 84 | {pos: [0, 3, 4], state: "minecraft:glass"}, 85 | {pos: [1, 3, 0], state: "minecraft:glass"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:glass"}, 90 | {pos: [2, 3, 0], state: "minecraft:glass"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:glass"}, 95 | {pos: [3, 3, 0], state: "minecraft:glass"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:glass"}, 100 | {pos: [4, 3, 0], state: "minecraft:glass"}, 101 | {pos: [4, 3, 1], state: "minecraft:glass"}, 102 | {pos: [4, 3, 2], state: "minecraft:glass"}, 103 | {pos: [4, 3, 3], state: "minecraft:glass"}, 104 | {pos: [4, 3, 4], state: "minecraft:glass"}, 105 | {pos: [0, 4, 0], state: "minecraft:polished_andesite"}, 106 | {pos: [0, 4, 1], state: "minecraft:polished_andesite"}, 107 | {pos: [0, 4, 2], state: "minecraft:polished_andesite"}, 108 | {pos: [0, 4, 3], state: "minecraft:polished_andesite"}, 109 | {pos: [0, 4, 4], state: "minecraft:polished_andesite"}, 110 | {pos: [1, 4, 0], state: "minecraft:polished_andesite"}, 111 | {pos: [1, 4, 1], state: "minecraft:polished_andesite"}, 112 | {pos: [1, 4, 2], state: "minecraft:polished_andesite"}, 113 | {pos: [1, 4, 3], state: "minecraft:polished_andesite"}, 114 | {pos: [1, 4, 4], state: "minecraft:polished_andesite"}, 115 | {pos: [2, 4, 0], state: "minecraft:polished_andesite"}, 116 | {pos: [2, 4, 1], state: "minecraft:polished_andesite"}, 117 | {pos: [2, 4, 2], state: "minecraft:polished_andesite"}, 118 | {pos: [2, 4, 3], state: "minecraft:polished_andesite"}, 119 | {pos: [2, 4, 4], state: "minecraft:polished_andesite"}, 120 | {pos: [3, 4, 0], state: "minecraft:polished_andesite"}, 121 | {pos: [3, 4, 1], state: "minecraft:polished_andesite"}, 122 | {pos: [3, 4, 2], state: "minecraft:polished_andesite"}, 123 | {pos: [3, 4, 3], state: "minecraft:polished_andesite"}, 124 | {pos: [3, 4, 4], state: "minecraft:polished_andesite"}, 125 | {pos: [4, 4, 0], state: "minecraft:polished_andesite"}, 126 | {pos: [4, 4, 1], state: "minecraft:polished_andesite"}, 127 | {pos: [4, 4, 2], state: "minecraft:polished_andesite"}, 128 | {pos: [4, 4, 3], state: "minecraft:polished_andesite"}, 129 | {pos: [4, 4, 4], state: "minecraft:polished_andesite"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:glass", 135 | "minecraft:air" 136 | ] 137 | } 138 | -------------------------------------------------------------------------------- /run/gameteststructures/minecarts.turn.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2699, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:air"}, 31 | {pos: [0, 1, 1], state: "minecraft:polished_granite"}, 32 | {pos: [0, 1, 2], state: "minecraft:air"}, 33 | {pos: [0, 1, 3], state: "minecraft:air"}, 34 | {pos: [0, 1, 4], state: "minecraft:air"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:powered_rail{powered:false,shape:east_west,waterlogged:false}"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:air"}, 40 | {pos: [2, 1, 0], state: "minecraft:air"}, 41 | {pos: [2, 1, 1], state: "minecraft:rail{shape:east_west,waterlogged:false}"}, 42 | {pos: [2, 1, 2], state: "minecraft:air"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:air"}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:rail{shape:south_west,waterlogged:false}"}, 47 | {pos: [3, 1, 2], state: "minecraft:rail{shape:north_south,waterlogged:false}"}, 48 | {pos: [3, 1, 3], state: "minecraft:polished_granite"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:air"}, 55 | {pos: [0, 2, 0], state: "minecraft:air"}, 56 | {pos: [0, 2, 1], state: "minecraft:oak_button{face:floor,facing:west,powered:false}"}, 57 | {pos: [0, 2, 2], state: "minecraft:air"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:air"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:air"}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [ 132 | {blockPos: [1, 1, 1], pos: [1.5d, 1.0625d, 1.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: -1s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [21.5d, -61.9375d, 49.5d], Rotation: [0.0f, 0.0f], UUID: [I; 462307940, 1013665068, -1130612282, -812194913], id: "minecraft:minecart"}} 133 | ], 134 | palette: [ 135 | "minecraft:polished_andesite", 136 | "minecraft:polished_granite", 137 | "minecraft:air", 138 | "minecraft:powered_rail{powered:false,shape:east_west,waterlogged:false}", 139 | "minecraft:rail{shape:east_west,waterlogged:false}", 140 | "minecraft:rail{shape:south_west,waterlogged:false}", 141 | "minecraft:rail{shape:north_south,waterlogged:false}", 142 | "minecraft:oak_button{face:floor,facing:west,powered:false}" 143 | ] 144 | } 145 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone.redstone2.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:red_terracotta"}, 31 | {pos: [0, 1, 1], state: "minecraft:repeater{delay:4,facing:north,locked:false,powered:false}"}, 32 | {pos: [0, 1, 2], state: "minecraft:repeater{delay:4,facing:north,locked:false,powered:false}"}, 33 | {pos: [0, 1, 3], state: "minecraft:repeater{delay:4,facing:north,locked:false,powered:false}"}, 34 | {pos: [0, 1, 4], state: "minecraft:sticky_piston{extended:false,facing:east}"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:redstone_block"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:redstone_block"}, 40 | {pos: [2, 1, 0], state: "minecraft:emerald_block"}, 41 | {pos: [2, 1, 1], state: "minecraft:repeater{delay:4,facing:south,locked:false,powered:false}"}, 42 | {pos: [2, 1, 2], state: "minecraft:air"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:air"}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:air"}, 55 | {pos: [0, 2, 0], state: "minecraft:air"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:air"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:slime_block"}, 63 | {pos: [1, 2, 3], state: "minecraft:slime_block"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:air"}, 68 | {pos: [2, 2, 3], state: "minecraft:slime_block"}, 69 | {pos: [2, 2, 4], state: "minecraft:sticky_piston{extended:false,facing:east}"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:slime_block"}, 74 | {pos: [3, 2, 4], state: "minecraft:slime_block"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:honey_block"}, 88 | {pos: [1, 3, 3], state: "minecraft:honey_block"}, 89 | {pos: [1, 3, 4], state: "minecraft:honey_block"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:honey_block"}, 93 | {pos: [2, 3, 3], state: "minecraft:honey_block"}, 94 | {pos: [2, 3, 4], state: "minecraft:honey_block"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:honey_block"}, 98 | {pos: [3, 3, 3], state: "minecraft:honey_block"}, 99 | {pos: [3, 3, 4], state: "minecraft:honey_block"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:honey_block"}, 113 | {pos: [1, 4, 3], state: "minecraft:honey_block"}, 114 | {pos: [1, 4, 4], state: "minecraft:honey_block"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:honey_block"}, 118 | {pos: [2, 4, 3], state: "minecraft:honey_block"}, 119 | {pos: [2, 4, 4], state: "minecraft:honey_block"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:honey_block"}, 123 | {pos: [3, 4, 3], state: "minecraft:honey_block"}, 124 | {pos: [3, 4, 4], state: "minecraft:honey_block"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:red_terracotta", 135 | "minecraft:sticky_piston{extended:false,facing:east}", 136 | "minecraft:redstone_block", 137 | "minecraft:emerald_block", 138 | "minecraft:slime_block", 139 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 140 | "minecraft:repeater{delay:4,facing:north,locked:false,powered:false}", 141 | "minecraft:air", 142 | "minecraft:repeater{delay:4,facing:south,locked:false,powered:false}", 143 | "minecraft:honey_block" 144 | ] 145 | } 146 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone.redstone1.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:red_terracotta"}, 31 | {pos: [0, 1, 1], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:up,west:none}"}, 32 | {pos: [0, 1, 2], state: "minecraft:polished_andesite"}, 33 | {pos: [0, 1, 3], state: "minecraft:air"}, 34 | {pos: [0, 1, 4], state: "minecraft:air"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:redstone_wall_torch{facing:east,lit:true}"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:air"}, 40 | {pos: [2, 1, 0], state: "minecraft:emerald_block"}, 41 | {pos: [2, 1, 1], state: "minecraft:comparator{facing:south,mode:compare,powered:false}", nbt: {OutputSignal: 0, id: "minecraft:comparator"}}, 42 | {pos: [2, 1, 2], state: "minecraft:hopper{enabled:false,facing:down}", nbt: {Items: [], TransferCooldown: 0, id: "minecraft:hopper"}}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:air"}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:air"}, 55 | {pos: [0, 2, 0], state: "minecraft:air"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [{Count: 1b, Slot: 0b, id: "minecraft:polished_andesite"}], id: "minecraft:chest"}}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:red_terracotta", 135 | "minecraft:emerald_block", 136 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 137 | "minecraft:redstone_wire{east:none,north:side,power:0,south:up,west:none}", 138 | "minecraft:air", 139 | "minecraft:redstone_wall_torch{facing:east,lit:true}", 140 | "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", 141 | "minecraft:comparator{facing:south,mode:compare,powered:false}", 142 | "minecraft:hopper{enabled:false,facing:down}", 143 | "minecraft:chest{facing:north,type:single,waterlogged:false}" 144 | ] 145 | } 146 | -------------------------------------------------------------------------------- /run/gameteststructures/example.waterflow1.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [6, 4, 6], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [0, 0, 5], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 15 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 16 | {pos: [1, 0, 5], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 20 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 21 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 22 | {pos: [2, 0, 5], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 25 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 26 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 27 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 28 | {pos: [3, 0, 5], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 30 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 31 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 32 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 33 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 34 | {pos: [4, 0, 5], state: "minecraft:polished_andesite"}, 35 | {pos: [5, 0, 0], state: "minecraft:polished_andesite"}, 36 | {pos: [5, 0, 1], state: "minecraft:polished_andesite"}, 37 | {pos: [5, 0, 2], state: "minecraft:polished_andesite"}, 38 | {pos: [5, 0, 3], state: "minecraft:polished_andesite"}, 39 | {pos: [5, 0, 4], state: "minecraft:polished_andesite"}, 40 | {pos: [5, 0, 5], state: "minecraft:polished_andesite"}, 41 | {pos: [0, 1, 0], state: "minecraft:glass"}, 42 | {pos: [0, 1, 1], state: "minecraft:glass"}, 43 | {pos: [0, 1, 2], state: "minecraft:glass"}, 44 | {pos: [0, 1, 3], state: "minecraft:glass"}, 45 | {pos: [0, 1, 4], state: "minecraft:glass"}, 46 | {pos: [0, 1, 5], state: "minecraft:glass"}, 47 | {pos: [1, 1, 0], state: "minecraft:glass"}, 48 | {pos: [1, 1, 1], state: "minecraft:glass"}, 49 | {pos: [1, 1, 2], state: "minecraft:glass"}, 50 | {pos: [1, 1, 3], state: "minecraft:glass"}, 51 | {pos: [1, 1, 4], state: "minecraft:glass"}, 52 | {pos: [1, 1, 5], state: "minecraft:glass"}, 53 | {pos: [2, 1, 0], state: "minecraft:glass"}, 54 | {pos: [2, 1, 1], state: "minecraft:glass"}, 55 | {pos: [2, 1, 2], state: "minecraft:air"}, 56 | {pos: [2, 1, 3], state: "minecraft:glass"}, 57 | {pos: [2, 1, 4], state: "minecraft:glass"}, 58 | {pos: [2, 1, 5], state: "minecraft:glass"}, 59 | {pos: [3, 1, 0], state: "minecraft:glass"}, 60 | {pos: [3, 1, 1], state: "minecraft:glass"}, 61 | {pos: [3, 1, 2], state: "minecraft:glass"}, 62 | {pos: [3, 1, 3], state: "minecraft:glass"}, 63 | {pos: [3, 1, 4], state: "minecraft:glass"}, 64 | {pos: [3, 1, 5], state: "minecraft:glass"}, 65 | {pos: [4, 1, 0], state: "minecraft:glass"}, 66 | {pos: [4, 1, 1], state: "minecraft:glass"}, 67 | {pos: [4, 1, 2], state: "minecraft:glass"}, 68 | {pos: [4, 1, 3], state: "minecraft:glass"}, 69 | {pos: [4, 1, 4], state: "minecraft:glass"}, 70 | {pos: [4, 1, 5], state: "minecraft:glass"}, 71 | {pos: [5, 1, 0], state: "minecraft:glass"}, 72 | {pos: [5, 1, 1], state: "minecraft:glass"}, 73 | {pos: [5, 1, 2], state: "minecraft:glass"}, 74 | {pos: [5, 1, 3], state: "minecraft:glass"}, 75 | {pos: [5, 1, 4], state: "minecraft:glass"}, 76 | {pos: [5, 1, 5], state: "minecraft:glass"}, 77 | {pos: [0, 2, 0], state: "minecraft:glass"}, 78 | {pos: [0, 2, 1], state: "minecraft:glass"}, 79 | {pos: [0, 2, 2], state: "minecraft:glass"}, 80 | {pos: [0, 2, 3], state: "minecraft:glass"}, 81 | {pos: [0, 2, 4], state: "minecraft:glass"}, 82 | {pos: [0, 2, 5], state: "minecraft:glass"}, 83 | {pos: [1, 2, 0], state: "minecraft:glass"}, 84 | {pos: [1, 2, 1], state: "minecraft:air"}, 85 | {pos: [1, 2, 2], state: "minecraft:air"}, 86 | {pos: [1, 2, 3], state: "minecraft:air"}, 87 | {pos: [1, 2, 4], state: "minecraft:air"}, 88 | {pos: [1, 2, 5], state: "minecraft:glass"}, 89 | {pos: [2, 2, 0], state: "minecraft:glass"}, 90 | {pos: [2, 2, 1], state: "minecraft:air"}, 91 | {pos: [2, 2, 2], state: "minecraft:air"}, 92 | {pos: [2, 2, 3], state: "minecraft:air"}, 93 | {pos: [2, 2, 4], state: "minecraft:air"}, 94 | {pos: [2, 2, 5], state: "minecraft:glass"}, 95 | {pos: [3, 2, 0], state: "minecraft:glass"}, 96 | {pos: [3, 2, 1], state: "minecraft:air"}, 97 | {pos: [3, 2, 2], state: "minecraft:air"}, 98 | {pos: [3, 2, 3], state: "minecraft:air"}, 99 | {pos: [3, 2, 4], state: "minecraft:air"}, 100 | {pos: [3, 2, 5], state: "minecraft:glass"}, 101 | {pos: [4, 2, 0], state: "minecraft:glass"}, 102 | {pos: [4, 2, 1], state: "minecraft:air"}, 103 | {pos: [4, 2, 2], state: "minecraft:air"}, 104 | {pos: [4, 2, 3], state: "minecraft:air"}, 105 | {pos: [4, 2, 4], state: "minecraft:air"}, 106 | {pos: [4, 2, 5], state: "minecraft:glass"}, 107 | {pos: [5, 2, 0], state: "minecraft:glass"}, 108 | {pos: [5, 2, 1], state: "minecraft:glass"}, 109 | {pos: [5, 2, 2], state: "minecraft:glass"}, 110 | {pos: [5, 2, 3], state: "minecraft:glass"}, 111 | {pos: [5, 2, 4], state: "minecraft:glass"}, 112 | {pos: [5, 2, 5], state: "minecraft:glass"}, 113 | {pos: [0, 3, 0], state: "minecraft:air"}, 114 | {pos: [0, 3, 1], state: "minecraft:air"}, 115 | {pos: [0, 3, 2], state: "minecraft:air"}, 116 | {pos: [0, 3, 3], state: "minecraft:air"}, 117 | {pos: [0, 3, 4], state: "minecraft:air"}, 118 | {pos: [0, 3, 5], state: "minecraft:air"}, 119 | {pos: [1, 3, 0], state: "minecraft:air"}, 120 | {pos: [1, 3, 1], state: "minecraft:air"}, 121 | {pos: [1, 3, 2], state: "minecraft:air"}, 122 | {pos: [1, 3, 3], state: "minecraft:air"}, 123 | {pos: [1, 3, 4], state: "minecraft:air"}, 124 | {pos: [1, 3, 5], state: "minecraft:air"}, 125 | {pos: [2, 3, 0], state: "minecraft:air"}, 126 | {pos: [2, 3, 1], state: "minecraft:air"}, 127 | {pos: [2, 3, 2], state: "minecraft:air"}, 128 | {pos: [2, 3, 3], state: "minecraft:air"}, 129 | {pos: [2, 3, 4], state: "minecraft:air"}, 130 | {pos: [2, 3, 5], state: "minecraft:air"}, 131 | {pos: [3, 3, 0], state: "minecraft:air"}, 132 | {pos: [3, 3, 1], state: "minecraft:air"}, 133 | {pos: [3, 3, 2], state: "minecraft:air"}, 134 | {pos: [3, 3, 3], state: "minecraft:air"}, 135 | {pos: [3, 3, 4], state: "minecraft:air"}, 136 | {pos: [3, 3, 5], state: "minecraft:air"}, 137 | {pos: [4, 3, 0], state: "minecraft:air"}, 138 | {pos: [4, 3, 1], state: "minecraft:air"}, 139 | {pos: [4, 3, 2], state: "minecraft:air"}, 140 | {pos: [4, 3, 3], state: "minecraft:air"}, 141 | {pos: [4, 3, 4], state: "minecraft:air"}, 142 | {pos: [4, 3, 5], state: "minecraft:air"}, 143 | {pos: [5, 3, 0], state: "minecraft:air"}, 144 | {pos: [5, 3, 1], state: "minecraft:air"}, 145 | {pos: [5, 3, 2], state: "minecraft:air"}, 146 | {pos: [5, 3, 3], state: "minecraft:air"}, 147 | {pos: [5, 3, 4], state: "minecraft:air"}, 148 | {pos: [5, 3, 5], state: "minecraft:air"} 149 | ], 150 | entities: [], 151 | palette: [ 152 | "minecraft:polished_andesite", 153 | "minecraft:glass", 154 | "minecraft:air" 155 | ] 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/util/GameTestUtil.java: -------------------------------------------------------------------------------- 1 | package mctester.common.util; 2 | 3 | import mctester.mixin.accessor.GameTestStateAccessor; 4 | import net.minecraft.block.BlockState; 5 | import net.minecraft.block.entity.StructureBlockBlockEntity; 6 | import net.minecraft.entity.Entity; 7 | import net.minecraft.entity.EntityType; 8 | import net.minecraft.entity.SpawnReason; 9 | import net.minecraft.entity.mob.MobEntity; 10 | import net.minecraft.nbt.NbtCompound; 11 | import net.minecraft.predicate.NbtPredicate; 12 | import net.minecraft.server.world.ServerWorld; 13 | import net.minecraft.structure.StructureTemplate; 14 | import net.minecraft.test.GameTestState; 15 | import net.minecraft.util.BlockMirror; 16 | import net.minecraft.util.BlockRotation; 17 | import net.minecraft.util.math.*; 18 | 19 | import java.util.List; 20 | import java.util.function.Predicate; 21 | import java.util.stream.Stream; 22 | 23 | public class GameTestUtil { 24 | public static BlockBox getTestBlockBox(GameTestState gameTest) { 25 | return getTestBlockBox(gameTest.getStructureBlockBlockEntity()); 26 | } 27 | 28 | public static Box getTestBox(GameTestState gameTest) { 29 | return getTestBox(gameTest.getStructureBlockBlockEntity()); 30 | } 31 | 32 | private static BlockBox getTestBlockBox(StructureBlockBlockEntity structureBlockEntity) { 33 | BlockPos blockPos = structureBlockEntity.getPos().add(structureBlockEntity.getOffset()); 34 | BlockPos blockPos2 = blockPos.add(structureBlockEntity.getSize().add(-1, -1, -1)); 35 | BlockPos blockPos3 = StructureTemplate.transformAround(blockPos2, BlockMirror.NONE, structureBlockEntity.getRotation(), blockPos); 36 | return BlockBox.create(blockPos, blockPos3); 37 | } 38 | 39 | private static Box getTestBox(StructureBlockBlockEntity structureBlockEntity) { 40 | BlockPos blockPos = structureBlockEntity.getPos().add(structureBlockEntity.getOffset()); 41 | BlockPos blockPos2 = blockPos.add(structureBlockEntity.getSize().add(-1, -1, -1)); 42 | BlockPos blockPos3 = StructureTemplate.transformAround(blockPos2, BlockMirror.NONE, structureBlockEntity.getRotation(), blockPos); 43 | return Box.enclosing(blockPos, blockPos3); 44 | } 45 | 46 | 47 | public static Box transformBox(GameTestState gameTest, Box box) { 48 | BlockPos blockPos = gameTest.getPos(); 49 | Vec3d corner1 = new Vec3d(box.minX, box.minY, box.minZ); 50 | Vec3d corner2 = new Vec3d(box.maxX, box.maxY, box.maxZ); 51 | 52 | corner1 = StructureTemplate.transformAround(corner1.add(blockPos.getX(), blockPos.getY(), blockPos.getZ()), BlockMirror.NONE, gameTest.getRotation(), blockPos); 53 | corner2 = StructureTemplate.transformAround(corner2.add(blockPos.getX(), blockPos.getY(), blockPos.getZ()), BlockMirror.NONE, gameTest.getRotation(), blockPos); 54 | return new Box(corner1, corner2); 55 | } 56 | 57 | public static Vec3d transformRelativeToAbsolutePos(GameTestState gameTest, Vec3d pos) { 58 | BlockPos basePos = gameTest.getPos(); 59 | return StructureTemplate.transformAround(pos.add(basePos.getX(), basePos.getY(), basePos.getZ()), BlockMirror.NONE, gameTest.getRotation(), basePos); 60 | } 61 | 62 | public static Vec3d transformRelativeToAbsolutePos(GameTestState gameTest, double x, double y, double z) { 63 | return transformRelativeToAbsolutePos(gameTest, new Vec3d(x, y, z)); 64 | } 65 | 66 | public static BlockPos transformRelativeToAbsolutePos(GameTestState gameTest, Vec3i pos) { 67 | BlockPos blockPos = gameTest.getPos(); 68 | return StructureTemplate.transformAround(blockPos.add(pos), BlockMirror.NONE, gameTest.getRotation(), blockPos); 69 | } 70 | 71 | public static BlockPos transformRelativeToAbsolutePos(GameTestState gameTest, int x, int y, int z) { 72 | BlockPos blockPos = gameTest.getPos(); 73 | return StructureTemplate.transformAround(blockPos.add(x, y, z), BlockMirror.NONE, gameTest.getRotation(), blockPos); 74 | } 75 | 76 | public static List getEntitiesInTestArea(GameTestState e) { 77 | return e.getWorld().getOtherEntities(null, GameTestUtil.getTestBox(e)); 78 | } 79 | 80 | public static List getEntitiesInBox(GameTestState e, Box box) { 81 | box = transformBox(e, box); 82 | return e.getWorld().getOtherEntities(null, GameTestUtil.transformBox(e, box)); 83 | } 84 | 85 | public static List getEntitiesInBox(GameTestState e, EntityType entityType, Box box) { 86 | box = transformBox(e, box); 87 | return e.getWorld().getEntitiesByType(entityType, box, t -> true); 88 | } 89 | 90 | public static List getEntitiesInBox(GameTestState e, EntityType entityType, Box box, Predicate entityPredicate) { 91 | box = transformBox(e, box); 92 | if (entityPredicate == null) { 93 | entityPredicate = t -> true; 94 | } 95 | return e.getWorld().getEntitiesByType(entityType, box, entityPredicate); 96 | } 97 | 98 | public static T spawnEntityWithTransforms(GameTestState gameTest, double x, double y, double z, EntityType entityType, NbtCompound entityTag) { 99 | ServerWorld serverWorld = gameTest.getWorld(); 100 | T entity = entityType.create(serverWorld); 101 | if (entity == null) { 102 | throw new IllegalStateException("Could not create entity of Type " + entityType); 103 | } 104 | if (entity instanceof MobEntity) { 105 | ((MobEntity) entity).initialize(serverWorld, serverWorld.getLocalDifficulty(entity.getBlockPos()), SpawnReason.COMMAND, null); 106 | } 107 | 108 | if (entityTag != null) { 109 | NbtCompound newTag = NbtPredicate.entityToNbt(entity).copy().copyFrom(entityTag); 110 | entity.readNbt(newTag); 111 | } 112 | 113 | BlockPos blockPos = gameTest.getPos(); 114 | Vec3d pos2 = new Vec3d(x, y, z); 115 | //transform the position so the spawning works 116 | BlockRotation blockRotation = gameTest.getRotation(); 117 | Vec3d pos3 = StructureTemplate.transformAround(pos2.add(blockPos.getX(), blockPos.getY(), blockPos.getZ()), BlockMirror.NONE, blockRotation, blockPos); 118 | float yaw = entity.getYaw(); 119 | switch (blockRotation) { 120 | case NONE: 121 | break; 122 | case CLOCKWISE_90: 123 | yaw = (yaw + 90f) % 360; 124 | break; 125 | case CLOCKWISE_180: 126 | yaw = (yaw + 180f) % 360; 127 | break; 128 | case COUNTERCLOCKWISE_90: 129 | yaw = (yaw - 90f) % 360; 130 | break; 131 | } 132 | entity.refreshPositionAndAngles(pos3.getX(), pos3.getY(), pos3.getZ(), yaw, entity.getPitch()); 133 | serverWorld.spawnEntity(entity); 134 | return entity; 135 | } 136 | 137 | public static void setBlockStateWithTransforms(GameTestState gameTest, int x, int y, int z, BlockState blockState) { 138 | ServerWorld serverWorld = gameTest.getWorld(); 139 | BlockPos blockPos = gameTest.getPos(); 140 | BlockPos blockPos2 = new BlockPos(x, y, z); 141 | BlockPos blockPos3 = StructureTemplate.transformAround(blockPos.add(blockPos2), BlockMirror.NONE, gameTest.getRotation(), blockPos); 142 | serverWorld.setBlockState(blockPos3, blockState.rotate(gameTest.getRotation())); 143 | } 144 | 145 | public static BlockState getBlockStateWithTransforms(GameTestState gameTest, int x, int y, int z) { 146 | ServerWorld serverWorld = gameTest.getWorld(); 147 | BlockPos blockPos = gameTest.getPos(); 148 | BlockPos blockPos2 = new BlockPos(x, y, z); 149 | BlockPos blockPos3 = StructureTemplate.transformAround(blockPos.add(blockPos2), BlockMirror.NONE, gameTest.getRotation(), blockPos); 150 | return serverWorld.getBlockState(blockPos3).rotate(getInverse(gameTest.getRotation())); 151 | } 152 | 153 | public static Stream streamPositions(GameTestState gameTest) { 154 | return BlockPos.stream(GameTestUtil.getTestBlockBox(gameTest)); 155 | } 156 | 157 | public static BlockRotation getInverse(BlockRotation blockRotation) { 158 | switch (blockRotation) { 159 | case NONE: 160 | return BlockRotation.NONE; 161 | case CLOCKWISE_90: 162 | return BlockRotation.COUNTERCLOCKWISE_90; 163 | case CLOCKWISE_180: 164 | return BlockRotation.CLOCKWISE_180; 165 | case COUNTERCLOCKWISE_90: 166 | return BlockRotation.CLOCKWISE_90; 167 | default: 168 | throw new IllegalStateException("Unexpected value: " + blockRotation); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone.redstone4.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:red_terracotta"}, 31 | {pos: [0, 1, 1], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:up,west:none}"}, 32 | {pos: [0, 1, 2], state: "minecraft:polished_andesite"}, 33 | {pos: [0, 1, 3], state: "minecraft:redstone_wall_torch{facing:south,lit:true}"}, 34 | {pos: [0, 1, 4], state: "minecraft:air"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:redstone_wire{east:side,north:none,power:15,south:none,west:side}"}, 39 | {pos: [1, 1, 4], state: "minecraft:air"}, 40 | {pos: [2, 1, 0], state: "minecraft:polished_andesite"}, 41 | {pos: [2, 1, 1], state: "minecraft:comparator{facing:south,mode:compare,powered:true}", nbt: {OutputSignal: 1, id: "minecraft:comparator"}}, 42 | {pos: [2, 1, 2], state: "minecraft:dropper{facing:up,triggered:true}", nbt: {Items: [{Count: 15b, Slot: 4b, id: "minecraft:redstone"}], id: "minecraft:dropper"}}, 43 | {pos: [2, 1, 3], state: "minecraft:polished_andesite"}, 44 | {pos: [2, 1, 4], state: "minecraft:air"}, 45 | {pos: [3, 1, 0], state: "minecraft:emerald_block"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:redstone_wall_torch{facing:east,lit:false}"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:air"}, 55 | {pos: [0, 2, 0], state: "minecraft:lever{face:floor,facing:south,powered:false}"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:polished_andesite"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:redstone_torch{lit:false}"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [{Count: 1b, Slot: 0b, id: "minecraft:redstone"}], id: "minecraft:chest"}}, 68 | {pos: [2, 2, 3], state: "minecraft:repeater{delay:1,facing:east,locked:false,powered:false}"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:polished_andesite"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:red_terracotta", 135 | "minecraft:emerald_block", 136 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 137 | "minecraft:redstone_wire{east:none,north:side,power:0,south:up,west:none}", 138 | "minecraft:redstone_wall_torch{facing:south,lit:true}", 139 | "minecraft:air", 140 | "minecraft:redstone_wire{east:side,north:none,power:15,south:none,west:side}", 141 | "minecraft:redstone_wall_torch{facing:east,lit:false}", 142 | "minecraft:lever{face:floor,facing:south,powered:false}", 143 | "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", 144 | "minecraft:redstone_torch{lit:false}", 145 | "minecraft:repeater{delay:1,facing:east,locked:false,powered:false}", 146 | "minecraft:comparator{facing:south,mode:compare,powered:true}", 147 | "minecraft:dropper{facing:up,triggered:true}", 148 | "minecraft:chest{facing:north,type:single,waterlogged:false}" 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone.redstone3.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:red_terracotta"}, 31 | {pos: [0, 1, 1], state: "minecraft:comparator{facing:north,mode:subtract,powered:false}", nbt: {OutputSignal: 0, id: "minecraft:comparator"}}, 32 | {pos: [0, 1, 2], state: "minecraft:redstone_wire{east:side,north:side,power:0,south:side,west:none}"}, 33 | {pos: [0, 1, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 34 | {pos: [0, 1, 4], state: "minecraft:redstone_wire{east:side,north:side,power:0,south:none,west:none}"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:redstone_wire{east:side,north:none,power:0,south:side,west:side}"}, 37 | {pos: [1, 1, 2], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}"}, 38 | {pos: [1, 1, 3], state: "minecraft:glass"}, 39 | {pos: [1, 1, 4], state: "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}"}, 40 | {pos: [2, 1, 0], state: "minecraft:air"}, 41 | {pos: [2, 1, 1], state: "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}"}, 42 | {pos: [2, 1, 2], state: "minecraft:glass"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:dispenser{facing:north,triggered:false}", nbt: {Items: [{Count: 32b, Slot: 4b, id: "minecraft:redstone"}], id: "minecraft:dispenser"}}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:glass"}, 49 | {pos: [3, 1, 4], state: "minecraft:comparator{facing:west,mode:compare,powered:true}", nbt: {OutputSignal: 1, id: "minecraft:comparator"}}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:redstone_wire{east:none,north:none,power:0,south:side,west:side}"}, 52 | {pos: [4, 1, 2], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 53 | {pos: [4, 1, 3], state: "minecraft:redstone_wall_torch{facing:north,lit:false}"}, 54 | {pos: [4, 1, 4], state: "minecraft:polished_andesite"}, 55 | {pos: [0, 2, 0], state: "minecraft:air"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:air"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:air"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:air"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:air"}, 68 | {pos: [2, 2, 3], state: "minecraft:glass"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:air"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:emerald_block"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:air"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:air"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:air"}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [], 132 | palette: [ 133 | "minecraft:polished_andesite", 134 | "minecraft:red_terracotta", 135 | "minecraft:glass", 136 | "minecraft:emerald_block", 137 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 138 | "minecraft:redstone_wire{east:side,north:side,power:0,south:side,west:none}", 139 | "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", 140 | "minecraft:redstone_wire{east:side,north:side,power:0,south:none,west:none}", 141 | "minecraft:air", 142 | "minecraft:redstone_wire{east:side,north:none,power:0,south:side,west:side}", 143 | "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}", 144 | "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}", 145 | "minecraft:redstone_wire{east:none,north:none,power:0,south:side,west:side}", 146 | "minecraft:redstone_wall_torch{facing:north,lit:false}", 147 | "minecraft:comparator{facing:north,mode:subtract,powered:false}", 148 | "minecraft:dispenser{facing:north,triggered:false}", 149 | "minecraft:comparator{facing:west,mode:compare,powered:true}" 150 | ] 151 | } 152 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone.redstone5.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2687, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:red_terracotta"}, 31 | {pos: [0, 1, 1], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 32 | {pos: [0, 1, 2], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 33 | {pos: [0, 1, 3], state: "minecraft:redstone_wire{east:up,north:side,power:0,south:side,west:none}"}, 34 | {pos: [0, 1, 4], state: "minecraft:redstone_wire{east:side,north:side,power:0,south:none,west:none}"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:glass"}, 37 | {pos: [1, 1, 2], state: "minecraft:glass"}, 38 | {pos: [1, 1, 3], state: "minecraft:polished_andesite"}, 39 | {pos: [1, 1, 4], state: "minecraft:repeater{delay:4,facing:west,locked:false,powered:false}"}, 40 | {pos: [2, 1, 0], state: "minecraft:polished_andesite"}, 41 | {pos: [2, 1, 1], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}"}, 42 | {pos: [2, 1, 2], state: "minecraft:stone_pressure_plate{powered:false}"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:dispenser{facing:north,triggered:false}", nbt: {Items: [{Count: 1b, Slot: 4b, id: "minecraft:arrow"}], id: "minecraft:dispenser"}}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:emerald_block"}, 47 | {pos: [3, 1, 2], state: "minecraft:glass"}, 48 | {pos: [3, 1, 3], state: "minecraft:glass"}, 49 | {pos: [3, 1, 4], state: "minecraft:air"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:air"}, 53 | {pos: [4, 1, 3], state: "minecraft:air"}, 54 | {pos: [4, 1, 4], state: "minecraft:air"}, 55 | {pos: [0, 2, 0], state: "minecraft:lever{face:floor,facing:south,powered:false}"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:air"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:target{power:0}"}, 63 | {pos: [1, 2, 3], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:sticky_piston{extended:false,facing:east}"}, 66 | {pos: [2, 2, 1], state: "minecraft:glass"}, 67 | {pos: [2, 2, 2], state: "minecraft:oak_trapdoor{facing:west,half:bottom,open:false,powered:false,waterlogged:false}"}, 68 | {pos: [2, 2, 3], state: "minecraft:glass"}, 69 | {pos: [2, 2, 4], state: "minecraft:air"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 72 | {pos: [3, 2, 2], state: "minecraft:glass"}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:redstone_block"}, 76 | {pos: [4, 2, 1], state: "minecraft:air"}, 77 | {pos: [4, 2, 2], state: "minecraft:air"}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:air"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:glass"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:glass"}, 92 | {pos: [2, 3, 2], state: "minecraft:air"}, 93 | {pos: [2, 3, 3], state: "minecraft:glass"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:glass"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:air"}, 101 | {pos: [4, 3, 1], state: "minecraft:air"}, 102 | {pos: [4, 3, 2], state: "minecraft:air"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:glass"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:glass"}, 117 | {pos: [2, 4, 2], state: "minecraft:smooth_stone_slab{type:top,waterlogged:false}"}, 118 | {pos: [2, 4, 3], state: "minecraft:glass"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:air"}, 122 | {pos: [3, 4, 2], state: "minecraft:glass"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:air"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [ 132 | {blockPos: [2, 2, 2], pos: [2.5d, 2.1875d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 0b, Invulnerable: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], NoBasePlate: 0b, OnGround: 1b, PortalCooldown: 0, Pos: [119.5d, 8.1875d, 69.5d], Pose: {Body: [0.0f, 3.8916645f, 0.0f], Head: [1.3138375f, -7.276938f, 0.0f]}, Rotation: [90.0f, 0.0f], ShowArms: 0b, Small: 0b, UUID: [I; -1004989423, 2125546769, -1443389755, -1818920823], id: "minecraft:armor_stand"}} 133 | ], 134 | palette: [ 135 | "minecraft:polished_andesite", 136 | "minecraft:red_terracotta", 137 | "minecraft:glass", 138 | "minecraft:emerald_block", 139 | "minecraft:target{power:0}", 140 | "minecraft:sticky_piston{extended:false,facing:east}", 141 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 142 | "minecraft:redstone_block", 143 | "minecraft:redstone_wire{east:none,north:side,power:0,south:side,west:none}", 144 | "minecraft:redstone_wire{east:up,north:side,power:0,south:side,west:none}", 145 | "minecraft:redstone_wire{east:side,north:side,power:0,south:none,west:none}", 146 | "minecraft:air", 147 | "minecraft:repeater{delay:4,facing:west,locked:false,powered:false}", 148 | "minecraft:stone_pressure_plate{powered:false}", 149 | "minecraft:lever{face:floor,facing:south,powered:false}", 150 | "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}", 151 | "minecraft:oak_trapdoor{facing:west,half:bottom,open:false,powered:false,waterlogged:false}", 152 | "minecraft:smooth_stone_slab{type:top,waterlogged:false}", 153 | "minecraft:dispenser{facing:north,triggered:false}" 154 | ] 155 | } 156 | -------------------------------------------------------------------------------- /run/gameteststructures/test_redstone_hoppercart.snbt: -------------------------------------------------------------------------------- 1 | { 2 | DataVersion: 2703, 3 | size: [5, 5, 5], 4 | data: [ 5 | {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, 6 | {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, 7 | {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, 8 | {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, 9 | {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, 10 | {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, 11 | {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, 12 | {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, 13 | {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, 14 | {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, 15 | {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, 16 | {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, 17 | {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, 18 | {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, 19 | {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, 20 | {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, 21 | {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, 22 | {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, 23 | {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, 24 | {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, 25 | {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, 26 | {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, 27 | {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, 28 | {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, 29 | {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, 30 | {pos: [0, 1, 0], state: "minecraft:air"}, 31 | {pos: [0, 1, 1], state: "minecraft:air"}, 32 | {pos: [0, 1, 2], state: "minecraft:air"}, 33 | {pos: [0, 1, 3], state: "minecraft:air"}, 34 | {pos: [0, 1, 4], state: "minecraft:air"}, 35 | {pos: [1, 1, 0], state: "minecraft:air"}, 36 | {pos: [1, 1, 1], state: "minecraft:air"}, 37 | {pos: [1, 1, 2], state: "minecraft:air"}, 38 | {pos: [1, 1, 3], state: "minecraft:air"}, 39 | {pos: [1, 1, 4], state: "minecraft:air"}, 40 | {pos: [2, 1, 0], state: "minecraft:air"}, 41 | {pos: [2, 1, 1], state: "minecraft:air"}, 42 | {pos: [2, 1, 2], state: "minecraft:air"}, 43 | {pos: [2, 1, 3], state: "minecraft:air"}, 44 | {pos: [2, 1, 4], state: "minecraft:emerald_block"}, 45 | {pos: [3, 1, 0], state: "minecraft:air"}, 46 | {pos: [3, 1, 1], state: "minecraft:air"}, 47 | {pos: [3, 1, 2], state: "minecraft:air"}, 48 | {pos: [3, 1, 3], state: "minecraft:air"}, 49 | {pos: [3, 1, 4], state: "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}"}, 50 | {pos: [4, 1, 0], state: "minecraft:air"}, 51 | {pos: [4, 1, 1], state: "minecraft:air"}, 52 | {pos: [4, 1, 2], state: "minecraft:dropper{facing:up,triggered:false}", nbt: {Items: [], id: "minecraft:dropper"}}, 53 | {pos: [4, 1, 3], state: "minecraft:comparator{facing:north,mode:compare,powered:false}", nbt: {OutputSignal: 0, id: "minecraft:comparator"}}, 54 | {pos: [4, 1, 4], state: "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}"}, 55 | {pos: [0, 2, 0], state: "minecraft:air"}, 56 | {pos: [0, 2, 1], state: "minecraft:air"}, 57 | {pos: [0, 2, 2], state: "minecraft:polished_andesite"}, 58 | {pos: [0, 2, 3], state: "minecraft:air"}, 59 | {pos: [0, 2, 4], state: "minecraft:air"}, 60 | {pos: [1, 2, 0], state: "minecraft:air"}, 61 | {pos: [1, 2, 1], state: "minecraft:air"}, 62 | {pos: [1, 2, 2], state: "minecraft:redstone_block"}, 63 | {pos: [1, 2, 3], state: "minecraft:air"}, 64 | {pos: [1, 2, 4], state: "minecraft:air"}, 65 | {pos: [2, 2, 0], state: "minecraft:air"}, 66 | {pos: [2, 2, 1], state: "minecraft:air"}, 67 | {pos: [2, 2, 2], state: "minecraft:polished_andesite"}, 68 | {pos: [2, 2, 3], state: "minecraft:air"}, 69 | {pos: [2, 2, 4], state: "minecraft:note_block{instrument:bit,note:0,powered:false}"}, 70 | {pos: [3, 2, 0], state: "minecraft:air"}, 71 | {pos: [3, 2, 1], state: "minecraft:air"}, 72 | {pos: [3, 2, 2], state: "minecraft:hopper{enabled:true,facing:east}", nbt: {Items: [], TransferCooldown: 0, id: "minecraft:hopper"}}, 73 | {pos: [3, 2, 3], state: "minecraft:air"}, 74 | {pos: [3, 2, 4], state: "minecraft:air"}, 75 | {pos: [4, 2, 0], state: "minecraft:air"}, 76 | {pos: [4, 2, 1], state: "minecraft:hopper{enabled:true,facing:south}", nbt: {Items: [], TransferCooldown: 0, id: "minecraft:hopper"}}, 77 | {pos: [4, 2, 2], state: "minecraft:hopper{enabled:true,facing:down}", nbt: {Items: [], TransferCooldown: 0, id: "minecraft:hopper"}}, 78 | {pos: [4, 2, 3], state: "minecraft:air"}, 79 | {pos: [4, 2, 4], state: "minecraft:air"}, 80 | {pos: [0, 3, 0], state: "minecraft:air"}, 81 | {pos: [0, 3, 1], state: "minecraft:air"}, 82 | {pos: [0, 3, 2], state: "minecraft:polished_andesite"}, 83 | {pos: [0, 3, 3], state: "minecraft:air"}, 84 | {pos: [0, 3, 4], state: "minecraft:air"}, 85 | {pos: [1, 3, 0], state: "minecraft:air"}, 86 | {pos: [1, 3, 1], state: "minecraft:air"}, 87 | {pos: [1, 3, 2], state: "minecraft:powered_rail{powered:true,shape:east_west,waterlogged:false}"}, 88 | {pos: [1, 3, 3], state: "minecraft:air"}, 89 | {pos: [1, 3, 4], state: "minecraft:air"}, 90 | {pos: [2, 3, 0], state: "minecraft:air"}, 91 | {pos: [2, 3, 1], state: "minecraft:air"}, 92 | {pos: [2, 3, 2], state: "minecraft:powered_rail{powered:true,shape:east_west,waterlogged:false}"}, 93 | {pos: [2, 3, 3], state: "minecraft:air"}, 94 | {pos: [2, 3, 4], state: "minecraft:air"}, 95 | {pos: [3, 3, 0], state: "minecraft:air"}, 96 | {pos: [3, 3, 1], state: "minecraft:air"}, 97 | {pos: [3, 3, 2], state: "minecraft:activator_rail{powered:false,shape:east_west,waterlogged:false}"}, 98 | {pos: [3, 3, 3], state: "minecraft:air"}, 99 | {pos: [3, 3, 4], state: "minecraft:air"}, 100 | {pos: [4, 3, 0], state: "minecraft:polished_andesite"}, 101 | {pos: [4, 3, 1], state: "minecraft:powered_rail{powered:false,shape:north_south,waterlogged:false}"}, 102 | {pos: [4, 3, 2], state: "minecraft:rail{shape:north_west,waterlogged:false}"}, 103 | {pos: [4, 3, 3], state: "minecraft:air"}, 104 | {pos: [4, 3, 4], state: "minecraft:air"}, 105 | {pos: [0, 4, 0], state: "minecraft:air"}, 106 | {pos: [0, 4, 1], state: "minecraft:air"}, 107 | {pos: [0, 4, 2], state: "minecraft:air"}, 108 | {pos: [0, 4, 3], state: "minecraft:air"}, 109 | {pos: [0, 4, 4], state: "minecraft:air"}, 110 | {pos: [1, 4, 0], state: "minecraft:air"}, 111 | {pos: [1, 4, 1], state: "minecraft:air"}, 112 | {pos: [1, 4, 2], state: "minecraft:air"}, 113 | {pos: [1, 4, 3], state: "minecraft:air"}, 114 | {pos: [1, 4, 4], state: "minecraft:air"}, 115 | {pos: [2, 4, 0], state: "minecraft:air"}, 116 | {pos: [2, 4, 1], state: "minecraft:air"}, 117 | {pos: [2, 4, 2], state: "minecraft:chest{facing:south,type:single,waterlogged:false}", nbt: {Items: [{Count: 6b, Slot: 0b, id: "minecraft:white_wool"}, {Count: 6b, Slot: 1b, id: "minecraft:orange_wool"}, {Count: 6b, Slot: 2b, id: "minecraft:magenta_wool"}, {Count: 6b, Slot: 3b, id: "minecraft:light_blue_wool"}, {Count: 6b, Slot: 4b, id: "minecraft:yellow_wool"}, {Count: 6b, Slot: 5b, id: "minecraft:lime_wool"}, {Count: 6b, Slot: 6b, id: "minecraft:pink_wool"}, {Count: 6b, Slot: 7b, id: "minecraft:gray_wool"}, {Count: 6b, Slot: 8b, id: "minecraft:light_gray_wool"}], id: "minecraft:chest"}}, 118 | {pos: [2, 4, 3], state: "minecraft:air"}, 119 | {pos: [2, 4, 4], state: "minecraft:air"}, 120 | {pos: [3, 4, 0], state: "minecraft:air"}, 121 | {pos: [3, 4, 1], state: "minecraft:lever{face:wall,facing:west,powered:false}"}, 122 | {pos: [3, 4, 2], state: "minecraft:air"}, 123 | {pos: [3, 4, 3], state: "minecraft:air"}, 124 | {pos: [3, 4, 4], state: "minecraft:air"}, 125 | {pos: [4, 4, 0], state: "minecraft:air"}, 126 | {pos: [4, 4, 1], state: "minecraft:red_terracotta"}, 127 | {pos: [4, 4, 2], state: "minecraft:air"}, 128 | {pos: [4, 4, 3], state: "minecraft:air"}, 129 | {pos: [4, 4, 4], state: "minecraft:air"} 130 | ], 131 | entities: [ 132 | {blockPos: [4, 3, 1], pos: [4.5d, 3.0625d, 1.5d], nbt: {Air: 300s, Enabled: 1b, FallDistance: 0.0f, Fire: -1s, Invulnerable: 0b, Items: [], Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [210.5d, -55.9375d, -146.5d], Rotation: [0.0f, 0.0f], TransferCooldown: 0, UUID: [I; 2123310111, -1814609094, -1749997798, -392317917], id: "minecraft:hopper_minecart"}} 133 | ], 134 | palette: [ 135 | "minecraft:polished_andesite", 136 | "minecraft:emerald_block", 137 | "minecraft:redstone_block", 138 | "minecraft:note_block{instrument:bit,note:0,powered:false}", 139 | "minecraft:red_terracotta", 140 | "minecraft:air", 141 | "minecraft:redstone_wire{east:side,north:none,power:0,south:none,west:side}", 142 | "minecraft:redstone_wire{east:none,north:side,power:0,south:none,west:side}", 143 | "minecraft:powered_rail{powered:true,shape:east_west,waterlogged:false}", 144 | "minecraft:activator_rail{powered:false,shape:east_west,waterlogged:false}", 145 | "minecraft:powered_rail{powered:false,shape:north_south,waterlogged:false}", 146 | "minecraft:rail{shape:north_west,waterlogged:false}", 147 | "minecraft:lever{face:wall,facing:west,powered:false}", 148 | "minecraft:dropper{facing:up,triggered:false}", 149 | "minecraft:comparator{facing:north,mode:compare,powered:false}", 150 | "minecraft:hopper{enabled:true,facing:east}", 151 | "minecraft:hopper{enabled:true,facing:south}", 152 | "minecraft:hopper{enabled:true,facing:down}", 153 | "minecraft:chest{facing:south,type:single,waterlogged:false}" 154 | ] 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/mctester/common/test/creation/GameTestHelper.java: -------------------------------------------------------------------------------- 1 | package mctester.common.test.creation; 2 | 3 | import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; 4 | import mctester.common.test.exceptions.GameTestAssertException; 5 | import mctester.common.test.exceptions.PreconditionNotMetException; 6 | import mctester.common.util.GameTestUtil; 7 | import mctester.common.util.TestFunctionWithVariant; 8 | import mctester.mixin.accessor.*; 9 | import net.minecraft.block.BlockState; 10 | import net.minecraft.block.ButtonBlock; 11 | import net.minecraft.entity.Entity; 12 | import net.minecraft.entity.EntityType; 13 | import net.minecraft.entity.ai.brain.Brain; 14 | import net.minecraft.entity.ai.pathing.Path; 15 | import net.minecraft.entity.mob.MobEntity; 16 | import net.minecraft.nbt.NbtCompound; 17 | import net.minecraft.test.GameTestException; 18 | import net.minecraft.test.GameTestState; 19 | import net.minecraft.test.PositionedException; 20 | import net.minecraft.test.TestContext; 21 | import net.minecraft.util.hit.BlockHitResult; 22 | import net.minecraft.util.math.BlockPos; 23 | import net.minecraft.util.math.Box; 24 | import net.minecraft.util.math.Direction; 25 | import net.minecraft.util.math.Vec3d; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Random; 30 | import java.util.function.*; 31 | 32 | @SuppressWarnings("unused") 33 | public class GameTestHelper { 34 | private static final Random RAND = new Random(); 35 | 36 | public final GameTestState gameTest; 37 | private final ArrayList> repeatedActions; 38 | private final Long2ReferenceOpenHashMap> tickActions; 39 | private final ArrayList> failReasons; 40 | public long currTick; 41 | 42 | public GameTestHelper(GameTestState gameTest) { 43 | this.gameTest = gameTest; 44 | if (this.gameTest instanceof GameTestAccess) { 45 | ((GameTestAccess) this.gameTest).mcTester$setTickCallback(this::handleTick); 46 | } 47 | this.repeatedActions = new ArrayList<>(); 48 | this.tickActions = new Long2ReferenceOpenHashMap<>(); 49 | this.failReasons = new ArrayList<>(); 50 | } 51 | 52 | public static GameTestHelper get(TestContext context) { 53 | return ((GameTestHelper.GameTestAccess) ((TestContextAccessor) context).getTest()).mcTester$getGameTestHelper(); 54 | } 55 | 56 | public int getVariant() { 57 | return ((TestFunctionWithVariant) (Object) this.gameTest.getTestFunction()).mcTester$getVariant(); 58 | } 59 | 60 | private void handleTick(long tick) { 61 | this.currTick = tick; 62 | Consumer tickAction = this.tickActions.get(tick); 63 | if (tickAction != null) { 64 | tickAction.accept(this); 65 | } 66 | if (!this.repeatedActions.isEmpty()) { 67 | for (BiConsumer gameTestHelperReferenceLongConsumer : this.repeatedActions) { 68 | gameTestHelperReferenceLongConsumer.accept(this, tick); 69 | } 70 | } 71 | if (!this.gameTest.isCompleted() && this.gameTest.getTestFunction().tickLimit() <= tick) { 72 | this.handleTimeout(); 73 | } 74 | } 75 | 76 | private void handleTimeout() { 77 | RuntimeException runtimeException = null; 78 | while (runtimeException == null) { 79 | if (this.failReasons.isEmpty()) { 80 | return; 81 | } 82 | runtimeException = this.failReasons.remove(RAND.nextInt(this.failReasons.size())).apply(this); 83 | } 84 | this.gameTest.fail(runtimeException); 85 | } 86 | 87 | public void addAction(long tick, Consumer action) { 88 | Consumer tickRunnable = this.tickActions.get(tick); 89 | if (tickRunnable == null) { 90 | tickRunnable = action; 91 | } else { 92 | tickRunnable = tickRunnable.andThen(action); 93 | } 94 | this.tickActions.put(tick, tickRunnable); 95 | } 96 | 97 | public void addRepeatedAction(BiConsumer runnable) { 98 | this.repeatedActions.add(runnable); 99 | } 100 | 101 | public void succeedWhen(BooleanSupplier successCondition) { 102 | this.addRepeatedAction( 103 | (GameTestHelper helper, Long tick) -> { 104 | if (!helper.gameTest.isCompleted()) { 105 | try { 106 | boolean isSuccess = successCondition.getAsBoolean(); 107 | if (isSuccess) { 108 | this.gameTest.fail(null); 109 | } 110 | } catch (PreconditionNotMetException ignored) { 111 | } catch (Exception e) { 112 | this.gameTest.fail(e); 113 | } 114 | } 115 | } 116 | ); 117 | } 118 | 119 | public void succeedWhen(BooleanSupplier successCondition, Function timeoutException) { 120 | this.succeedWhen(successCondition); 121 | if (timeoutException != null) { 122 | this.failReasons.add(timeoutException); 123 | } 124 | } 125 | 126 | public void succeedWhen(Function successCondition) { 127 | this.addRepeatedAction( 128 | (GameTestHelper helper, Long tick) -> { 129 | if (!helper.gameTest.isCompleted()) { 130 | try { 131 | boolean isSuccess = successCondition.apply(helper); 132 | if (isSuccess) { 133 | this.gameTest.fail(null); 134 | } 135 | } catch (PreconditionNotMetException ignored) { 136 | } catch (GameTestAssertException | GameTestException e) { 137 | this.gameTest.fail(e); 138 | } 139 | } 140 | } 141 | ); 142 | } 143 | 144 | public void succeedWhen(Function successCondition, Function timeoutException) { 145 | this.succeedWhen(successCondition); 146 | if (timeoutException != null) { 147 | this.failReasons.add(timeoutException); 148 | } 149 | } 150 | 151 | public void succeedWhen(BiFunction successCondition) { 152 | this.addRepeatedAction( 153 | (GameTestHelper helper, Long tick) -> { 154 | if (!helper.gameTest.isCompleted()) { 155 | try { 156 | boolean isSuccess = successCondition.apply(helper, tick); 157 | if (isSuccess) { 158 | this.gameTest.fail(null); 159 | } 160 | } catch (PreconditionNotMetException ignored) { 161 | } catch (GameTestAssertException | GameTestException e) { 162 | this.gameTest.fail(e); 163 | } 164 | } 165 | } 166 | ); 167 | } 168 | 169 | public void succeedWhen(BiFunction successCondition, Function timeoutException) { 170 | this.succeedWhen(successCondition); 171 | if (timeoutException != null) { 172 | this.failReasons.add(timeoutException); 173 | } 174 | } 175 | 176 | public void assertEntityPresent(EntityType entityType, BlockPos targetPos) { 177 | List entitiesInBox = GameTestUtil.getEntitiesInBox(this.gameTest, entityType, Box.enclosing(targetPos, targetPos)); 178 | if (entitiesInBox.isEmpty()) { 179 | throw PreconditionNotMetException.INSTANCE; //hack to get the same behavior as in the video (https://www.youtube.com/watch?v=vXaWOJTCYNg (11:15)) 180 | } 181 | } 182 | 183 | public void walkTo(MobEntity mob, BlockPos targetPos) { 184 | mob.setOnGround(true); 185 | targetPos = GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, targetPos); 186 | Path pathTo = mob.getNavigation().findPathTo(targetPos.getX() + 0.5D, targetPos.getY() + 1, targetPos.getZ() + 0.5D, 0); 187 | mob.getNavigation().startMovingAlong(pathTo, 1D); 188 | } 189 | 190 | public void walkTo(MobEntity mob, double x, double y, double z) { 191 | mob.setOnGround(true); 192 | Vec3d targetPos = GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, x, y, z); 193 | Path pathTo = mob.getNavigation().findPathTo(targetPos.getX() + 0.5D, targetPos.getY() + 1, targetPos.getZ() + 0.5D, 0); 194 | mob.getNavigation().startMovingAlong(pathTo, 1D); 195 | } 196 | 197 | public T spawnWithNoFreeWill(EntityType entityType, int x, int y, int z) { 198 | T entity = spawnEntity(x, y, z, entityType); 199 | 200 | //mobs have lots of brain, cut out the selection of tasks and goals 201 | if (entity instanceof MobEntity) { 202 | Brain brain = ((MobEntity) entity).getBrain(); 203 | ((BrainAccessor) brain).getTasks().clear(); 204 | } 205 | ((GoalSelectorAccessor) ((MobEntityAccessor) entity).getTargetSelector()).getGoals().clear(); 206 | ((GoalSelectorAccessor) ((MobEntityAccessor) entity).getGoalSelector()).getGoals().clear(); 207 | 208 | return entity; 209 | } 210 | 211 | public T spawnEntity(int x, int y, int z, EntityType entity) { 212 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x + 0.5D, y, z + 0.5D, entity, null); 213 | } 214 | 215 | public T spawnEntity(int x, int y, int z, EntityType entity, NbtCompound tag) { 216 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x + 0.5D, y, z + 0.5D, entity, tag); 217 | } 218 | 219 | public T spawnEntity(int x, int y, int z, EntityType entity, NbtCompound... entityTags) { 220 | NbtCompound tag = new NbtCompound(); 221 | for (NbtCompound entityTag : entityTags) { 222 | tag.copyFrom(entityTag); 223 | } 224 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x + 0.5D, y, z + 0.5D, entity, tag); 225 | } 226 | 227 | public T spawnEntity(double x, double y, double z, EntityType entity) { 228 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x, y, z, entity, null); 229 | } 230 | 231 | public T spawnEntity(double x, double y, double z, EntityType entity, NbtCompound entityTag) { 232 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x, y, z, entity, entityTag); 233 | } 234 | 235 | public T spawnEntity(double x, double y, double z, EntityType entity, NbtCompound... entityTags) { 236 | NbtCompound tag = new NbtCompound(); 237 | for (NbtCompound entityTag : entityTags) { 238 | tag.copyFrom(entityTag); 239 | } 240 | return GameTestUtil.spawnEntityWithTransforms(this.gameTest, x, y, z, entity, tag); 241 | } 242 | 243 | public void pressButton(int x, int y, int z) { 244 | BlockState blockState = this.getBlockState(x, y, z); 245 | if (blockState.getBlock() instanceof ButtonBlock) { 246 | BlockPos blockPos = GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, x, y, z); 247 | blockState.onUse(this.gameTest.getWorld(), null, new BlockHitResult(new Vec3d(blockPos.getX(), blockPos.getY(), blockPos.getZ()), Direction.DOWN, blockPos, false)); 248 | } else { 249 | throw new PositionedException("No pushable button found.", GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, new BlockPos(x, y, z)), new BlockPos(x, y, z), this.currTick); 250 | } 251 | } 252 | 253 | public void succeedWhenEntityCollides(EntityType entityType, int x, int y, int z) { 254 | this.succeedWhen((GameTestHelper helper) -> 255 | !GameTestUtil.getEntitiesInBox( 256 | this.gameTest, entityType, new Box(x, y, z, x + 1, y + 1, z + 1) 257 | ).isEmpty(), 258 | (GameTestHelper helper) -> new PositionedException("Expected " + entityType.getName().getString(), GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, new BlockPos(x, y, z)), new BlockPos(x, y, z), helper.currTick) 259 | ); 260 | } 261 | 262 | public void succeedWhenEntityCollides(EntityType entityType, Box box) { 263 | this.succeedWhen((GameTestHelper helper) -> 264 | !GameTestUtil.getEntitiesInBox( 265 | this.gameTest, entityType, box 266 | ).isEmpty(), 267 | (GameTestHelper helper) -> new PositionedException("Expected " + entityType.getName().getString(), GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, BlockPos.ofFloored(box.getCenter())), BlockPos.ofFloored(box.getCenter()), helper.currTick) 268 | ); 269 | } 270 | 271 | public void succeedWhenEntityPresent(EntityType entityType, int x, int y, int z) { 272 | this.succeedWhen((GameTestHelper helper) -> { 273 | List entitiesInBox = GameTestUtil.getEntitiesInBox( 274 | this.gameTest, entityType, new Box(x, y, z, x + 1, y + 1, z + 1) 275 | ); 276 | BlockPos targetPos = GameTestUtil.transformRelativeToAbsolutePos(helper.gameTest, x, y, z); 277 | for (int i = entitiesInBox.size() - 1; i >= 0; i--) { 278 | T entity = entitiesInBox.get(i); 279 | BlockPos blockPos = entity.getBlockPos(); 280 | if (targetPos.equals(blockPos)) { 281 | return true; 282 | } 283 | } 284 | return false; 285 | }, 286 | (GameTestHelper helper) -> new PositionedException("Expected " + entityType.getName().getString(), GameTestUtil.transformRelativeToAbsolutePos(this.gameTest, new BlockPos(x, y, z)), new BlockPos(x, y, z), helper.currTick) 287 | ); 288 | } 289 | 290 | public void setBlockState(int x, int y, int z, BlockState blockState) { 291 | GameTestUtil.setBlockStateWithTransforms(this.gameTest, x, y, z, blockState); 292 | } 293 | 294 | public BlockState getBlockState(int x, int y, int z) { 295 | return GameTestUtil.getBlockStateWithTransforms(this.gameTest, x, y, z); 296 | } 297 | 298 | public List getEntitiesInside() { 299 | return GameTestUtil.getEntitiesInTestArea(this.gameTest); 300 | } 301 | 302 | public interface GameTestAccess { 303 | GameTestHelper mcTester$getGameTestHelper(); 304 | 305 | void mcTester$setTickCallback(LongConsumer handler); 306 | } 307 | } 308 | --------------------------------------------------------------------------------