From 39ef3da5df0fad2054d8b95f15b51b4199479774 Mon Sep 17 00:00:00 2001 From: zelophed Date: Sat, 3 Jun 2023 12:45:24 +0200 Subject: [PATCH 1/4] updates to crowdin.yml and the localization workflow --- .github/workflows/localization.yml | 20 +++++++++++--------- crowdin.yml | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/localization.yml b/.github/workflows/localization.yml index ff318efe4..fd159edd4 100644 --- a/.github/workflows/localization.yml +++ b/.github/workflows/localization.yml @@ -4,15 +4,17 @@ name: Crowdin Action # Controls when the action will run. -on: +on: workflow_dispatch # Only run when started manually - workflow_dispatch: - inputs: - uploadTranslations: - description: "Set to true to upload (changed) translations to Crowdin" - type: boolean - required: true - default: false + + #: + # inputs: + # uploadTranslations: + # description: "Set to true to upload (changed) translations to Crowdin" + # type: boolean + # required: true + # default: false + #schedule: #- cron: '0 */6 * * *' # Every 6 hours - https://crontab.guru/#0_*/6_*_*_* @@ -30,7 +32,7 @@ jobs: # Upload sources to Crowdin upload_sources: true # Upload translations to Crowdin, only use true at initial run - upload_translations: ${{ github.event.inputs.uploadTranslations }} + upload_translations: false # Make pull request of Crowdin translations download_translations: true # To download translations to the specified version branch diff --git a/crowdin.yml b/crowdin.yml index 7ebdc939f..3f0119cba 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -11,6 +11,7 @@ "languages_mapping": { "locale_with_underscore": { "cs": "cs_cz", + "cy": "cy_gb", "da": "da_dk", "de": "de_de", "eo": "eo_uy", From 928fb9c890414954e4b90d3b7ad6abcefd72d0f4 Mon Sep 17 00:00:00 2001 From: TropheusJ Date: Fri, 30 Jun 2023 01:11:08 -0400 Subject: [PATCH 2/4] port new and updated tests from fabric --- .../bearing/MechanicalBearingBlockEntity.java | 3 + .../pulley/PulleyBlockEntity.java | 8 +- .../gametest/CreateGameTestHelper.java | 44 +++- .../gametest/tests/TestContraptions.java | 134 +++++++++++ .../gametest/tests/TestFluids.java | 220 +++++++++++++++--- .../gametest/tests/TestItems.java | 58 ++++- .../gametest/tests/TestMisc.java | 48 ++++ .../gametest/contraptions/controls.nbt | Bin 0 -> 1579 bytes .../gametest/contraptions/elevator.nbt | Bin 0 -> 1109 bytes .../contraptions/mounted_item_extract.nbt | Bin 1436 -> 1114 bytes .../gametest/contraptions/roller_filling.nbt | Bin 0 -> 1120 bytes .../roller_paving_and_clearing.nbt | Bin 0 -> 796 bytes .../gametest/fluids/hose_pulley_transfer.nbt | Bin 3467 -> 1710 bytes .../gametest/fluids/in_world_pumping_in.nbt | Bin 971 -> 970 bytes .../gametest/fluids/in_world_pumping_out.nbt | Bin 853 -> 948 bytes .../gametest/fluids/large_waterwheel.nbt | Bin 0 -> 804 bytes .../gametest/fluids/small_waterwheel.nbt | Bin 0 -> 687 bytes .../gametest/fluids/smart_observer_pipes.nbt | Bin 0 -> 1171 bytes .../gametest/fluids/threshold_switch.nbt | Bin 0 -> 1664 bytes .../gametest/fluids/waterwheel_materials.nbt | Bin 0 -> 1093 bytes .../gametest/items/attribute_filters.nbt | Bin 2151 -> 1901 bytes .../items/content_observer_counting.nbt | Bin 933 -> 0 bytes .../items/smart_observer_belt_and_funnel.nbt | Bin 0 -> 1661 bytes .../gametest/items/smart_observer_chutes.nbt | Bin 0 -> 628 bytes .../items/smart_observer_counting.nbt | Bin 0 -> 812 bytes .../items/smart_observer_filtered_storage.nbt | Bin 0 -> 638 bytes .../gametest/items/smart_observer_storage.nbt | Bin 0 -> 541 bytes ...ckpile_switch.nbt => threshold_switch.nbt} | Bin .../gametest/misc/netherite_backtank.nbt | Bin 0 -> 970 bytes .../gametest/misc/smart_observer_blocks.nbt | Bin 0 -> 507 bytes .../gametest/misc/threshold_switch_pulley.nbt | Bin 0 -> 839 bytes 31 files changed, 474 insertions(+), 41 deletions(-) create mode 100644 src/main/resources/data/create/structures/gametest/contraptions/controls.nbt create mode 100644 src/main/resources/data/create/structures/gametest/contraptions/elevator.nbt create mode 100644 src/main/resources/data/create/structures/gametest/contraptions/roller_filling.nbt create mode 100644 src/main/resources/data/create/structures/gametest/contraptions/roller_paving_and_clearing.nbt create mode 100644 src/main/resources/data/create/structures/gametest/fluids/large_waterwheel.nbt create mode 100644 src/main/resources/data/create/structures/gametest/fluids/small_waterwheel.nbt create mode 100644 src/main/resources/data/create/structures/gametest/fluids/smart_observer_pipes.nbt create mode 100644 src/main/resources/data/create/structures/gametest/fluids/threshold_switch.nbt create mode 100644 src/main/resources/data/create/structures/gametest/fluids/waterwheel_materials.nbt delete mode 100644 src/main/resources/data/create/structures/gametest/items/content_observer_counting.nbt create mode 100644 src/main/resources/data/create/structures/gametest/items/smart_observer_belt_and_funnel.nbt create mode 100644 src/main/resources/data/create/structures/gametest/items/smart_observer_chutes.nbt create mode 100644 src/main/resources/data/create/structures/gametest/items/smart_observer_counting.nbt create mode 100644 src/main/resources/data/create/structures/gametest/items/smart_observer_filtered_storage.nbt create mode 100644 src/main/resources/data/create/structures/gametest/items/smart_observer_storage.nbt rename src/main/resources/data/create/structures/gametest/items/{stockpile_switch.nbt => threshold_switch.nbt} (100%) create mode 100644 src/main/resources/data/create/structures/gametest/misc/netherite_backtank.nbt create mode 100644 src/main/resources/data/create/structures/gametest/misc/smart_observer_blocks.nbt create mode 100644 src/main/resources/data/create/structures/gametest/misc/threshold_switch_pulley.nbt diff --git a/src/main/java/com/simibubi/create/content/contraptions/bearing/MechanicalBearingBlockEntity.java b/src/main/java/com/simibubi/create/content/contraptions/bearing/MechanicalBearingBlockEntity.java index 98d1fc40b..d4c6c3e45 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/bearing/MechanicalBearingBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/bearing/MechanicalBearingBlockEntity.java @@ -353,4 +353,7 @@ public class MechanicalBearingBlockEntity extends GeneratingKineticBlockEntity angle = forcedAngle; } + public ControlledContraptionEntity getMovedContraption() { + return movedContraption; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/pulley/PulleyBlockEntity.java b/src/main/java/com/simibubi/create/content/contraptions/pulley/PulleyBlockEntity.java index c0a70d75f..2c425dd01 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/pulley/PulleyBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/pulley/PulleyBlockEntity.java @@ -163,7 +163,7 @@ public class PulleyBlockEntity extends LinearActuatorBlockEntity implements Thre } } } - + if (mirrorParent != null) removeRopes(); @@ -284,7 +284,7 @@ public class PulleyBlockEntity extends LinearActuatorBlockEntity implements Thre if (prevMirrorParent == null || !prevMirrorParent.equals(mirrorParent)) sharedMirrorContraption = null; } - + if (compound.contains("MirrorChildren")) mirrorChildren = NBTHelper.readCompoundList(compound.getList("MirrorChildren", Tag.TAG_COMPOUND), NbtUtils::readBlockPos); @@ -379,4 +379,8 @@ public class PulleyBlockEntity extends LinearActuatorBlockEntity implements Thre return 100; return 100 * getInterpolatedOffset(.5f) / distance; } + + public BlockPos getMirrorParent() { + return mirrorParent; + } } diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/CreateGameTestHelper.java b/src/main/java/com/simibubi/create/infrastructure/gametest/CreateGameTestHelper.java index 595f1289a..f9ae8c79d 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/CreateGameTestHelper.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/CreateGameTestHelper.java @@ -3,7 +3,19 @@ package com.simibubi.create.infrastructure.gametest; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import com.simibubi.create.content.contraptions.Contraption; +import com.simibubi.create.content.contraptions.actors.contraptionControls.ContraptionControlsMovement; +import com.simibubi.create.content.contraptions.actors.contraptionControls.ContraptionControlsMovingInteraction; +import com.simibubi.create.content.contraptions.behaviour.MovementContext; +import com.simibubi.create.content.kinetics.gauge.SpeedGaugeBlockEntity; + +import com.simibubi.create.content.kinetics.gauge.StressGaugeBlockEntity; + +import net.minecraftforge.registries.ForgeRegistries; + +import org.apache.commons.lang3.tuple.MutablePair; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -27,6 +39,7 @@ import net.minecraft.core.Registry; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.gametest.framework.GameTestInfo; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.ItemEntity; @@ -40,6 +53,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.phys.Vec3; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -80,7 +94,7 @@ public class CreateGameTestHelper extends GameTestHelper { public void flipBlock(BlockPos pos) { BlockState original = getBlockState(pos); if (!original.hasProperty(BlockStateProperties.FACING)) - fail("FACING property not in block: " + Registry.BLOCK.getId(original.getBlock())); + fail("FACING property not in block: " + ForgeRegistries.BLOCKS.getKey(original.getBlock())); Direction facing = original.getValue(BlockStateProperties.FACING); BlockState reversed = original.setValue(BlockStateProperties.FACING, facing.getOpposite()); setBlock(pos, reversed); @@ -123,6 +137,34 @@ public class CreateGameTestHelper extends GameTestHelper { behavior.setValue(mode.ordinal()); } + public void assertSpeedometerSpeed(BlockPos speedometer, float value) { + SpeedGaugeBlockEntity be = getBlockEntity(AllBlockEntityTypes.SPEEDOMETER.get(), speedometer); + assertInRange(be.getSpeed(), value - 0.01, value + 0.01); + } + + public void assertStressometerCapacity(BlockPos stressometer, float value) { + StressGaugeBlockEntity be = getBlockEntity(AllBlockEntityTypes.STRESSOMETER.get(), stressometer); + assertInRange(be.getNetworkCapacity(), value - 0.01, value + 0.01); + } + + public void toggleActorsOfType(Contraption contraption, ItemLike item) { + AtomicBoolean toggled = new AtomicBoolean(false); + contraption.getInteractors().forEach((localPos, behavior) -> { + if (toggled.get() || !(behavior instanceof ContraptionControlsMovingInteraction controls)) + return; + MutablePair actor = contraption.getActorAt(localPos); + if (actor == null) + return; + ItemStack filter = ContraptionControlsMovement.getFilter(actor.right); + if (filter != null && filter.is(item.asItem())) { + controls.handlePlayerInteraction( + makeMockPlayer(), InteractionHand.MAIN_HAND, localPos, contraption.entity + ); + toggled.set(true); + } + }); + } + // block entities /** diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestContraptions.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestContraptions.java index 6fdba39a1..c38956676 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestContraptions.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestContraptions.java @@ -1,11 +1,20 @@ package com.simibubi.create.infrastructure.gametest.tests; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import com.simibubi.create.AllBlockEntityTypes; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllEntityTypes; +import com.simibubi.create.content.contraptions.Contraption; +import com.simibubi.create.content.contraptions.bearing.MechanicalBearingBlockEntity; +import com.simibubi.create.content.contraptions.elevator.ElevatorPulleyBlockEntity; +import com.simibubi.create.content.kinetics.transmission.sequencer.SequencedGearshiftBlock; import com.simibubi.create.infrastructure.gametest.CreateGameTestHelper; import com.simibubi.create.infrastructure.gametest.GameTestGroup; import it.unimi.dsi.fastutil.objects.Object2LongMap; + import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTest; import net.minecraft.world.entity.EntityType; @@ -13,6 +22,9 @@ import net.minecraft.world.entity.projectile.Arrow; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.LeverBlock; +import net.minecraft.world.level.block.RedstoneLampBlock; import net.minecraftforge.fluids.FluidStack; @GameTestGroup(path = "contraptions") @@ -89,6 +101,128 @@ public class TestContraptions { helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.DIAMOND_BLOCK, end)); } + @GameTest(template = "controls", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void controls(CreateGameTestHelper helper) { + BlockPos button = new BlockPos(5, 5, 4); + BlockPos gearshift = new BlockPos(4, 5, 4); + BlockPos bearingPos = new BlockPos(4, 4, 4); + AtomicInteger step = new AtomicInteger(1); + + List dirt = List.of(new BlockPos(4, 2, 6), new BlockPos(2, 2, 4), new BlockPos(4, 2, 2)); + List wheat = List.of(new BlockPos(4, 3, 7), new BlockPos(1, 3, 4), new BlockPos(4, 3, 1)); + + helper.pressButton(button); + helper.succeedWhen(() -> { + // wait for gearshift to reset + helper.assertBlockProperty(gearshift, SequencedGearshiftBlock.STATE, 0); + if (step.get() == 4) + return; // step 4: all done! + MechanicalBearingBlockEntity bearing = helper.getBlockEntity(AllBlockEntityTypes.MECHANICAL_BEARING.get(), bearingPos); + if (bearing.getMovedContraption() == null) + helper.fail("Contraption not assembled"); + Contraption contraption = bearing.getMovedContraption().getContraption(); + switch (step.get()) { + case 1 -> { // step 1: both should be active + helper.assertBlockPresent(Blocks.FARMLAND, dirt.get(0)); + helper.assertBlockProperty(wheat.get(0), CropBlock.AGE, 0); + // now disable harvester + helper.toggleActorsOfType(contraption, AllBlocks.MECHANICAL_HARVESTER.get()); + helper.pressButton(button); + step.incrementAndGet(); + helper.fail("Entering step 2"); + } + case 2 -> { // step 2: harvester disabled + helper.assertBlockPresent(Blocks.FARMLAND, dirt.get(1)); + helper.assertBlockProperty(wheat.get(1), CropBlock.AGE, 7); + // now disable plough + helper.toggleActorsOfType(contraption, AllBlocks.MECHANICAL_PLOUGH.get()); + helper.pressButton(button); + step.incrementAndGet(); + helper.fail("Entering step 3"); + } + case 3 -> { // step 3: both disabled + helper.assertBlockPresent(Blocks.DIRT, dirt.get(2)); + helper.assertBlockProperty(wheat.get(2), CropBlock.AGE, 7); + // successful! + helper.pressButton(button); + step.incrementAndGet(); + helper.fail("Entering step 4"); + } + } + }); + } + + @GameTest(template = "elevator") + public static void elevator(CreateGameTestHelper helper) { + BlockPos pulley = new BlockPos(5, 12, 3); + BlockPos secondaryPulley = new BlockPos(5, 12, 1); + BlockPos bottomLamp = new BlockPos(2, 3, 2); + BlockPos topLamp = new BlockPos(2, 12, 2); + BlockPos lever = new BlockPos(1, 11, 2); + BlockPos elevatorStart = new BlockPos(4, 2, 2); + BlockPos cowSpawn = new BlockPos(4, 4, 2); + BlockPos cowEnd = new BlockPos(4, 13, 2); + + helper.runAtTickTime(1, () -> helper.spawn(EntityType.COW, cowSpawn)); + helper.runAtTickTime( + 15, () -> helper.getBlockEntity(AllBlockEntityTypes.ELEVATOR_PULLEY.get(), pulley).clicked() + ); + helper.succeedWhen(() -> { + helper.assertSecondsPassed(1); + if (!helper.getBlockState(lever).getValue(LeverBlock.POWERED)) { // step 1: check entity, lamps, and secondary, then move up + helper.getFirstEntity(AllEntityTypes.CONTROLLED_CONTRAPTION.get(), elevatorStart); // make sure entity exists + + helper.assertBlockProperty(topLamp, RedstoneLampBlock.LIT, false); + helper.assertBlockProperty(bottomLamp, RedstoneLampBlock.LIT, true); + + ElevatorPulleyBlockEntity secondary = helper.getBlockEntity(AllBlockEntityTypes.ELEVATOR_PULLEY.get(), secondaryPulley); + if (secondary.getMirrorParent() == null) + helper.fail("Secondary pulley has no parent"); + + helper.pullLever(lever); + helper.fail("Entering step 2"); + } else { // step 2: wait for top lamp and cow passenger + helper.assertBlockProperty(topLamp, RedstoneLampBlock.LIT, true); + helper.assertBlockProperty(bottomLamp, RedstoneLampBlock.LIT, false); + helper.assertEntityPresent(EntityType.COW, cowEnd); + // all done, disassemble + helper.getBlockEntity(AllBlockEntityTypes.ELEVATOR_PULLEY.get(), pulley).clicked(); + } + }); + } + + @GameTest(template = "roller_filling") + public static void rollerFilling(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(7, 6, 1); + BlockPos barrelEnd = new BlockPos(2, 5, 2); + List existing = BlockPos.betweenClosedStream(new BlockPos(1, 3, 2), new BlockPos(4, 2, 2)).toList(); + List filled = BlockPos.betweenClosedStream(new BlockPos(1, 2, 1), new BlockPos(4, 3, 3)) + .filter(pos -> !existing.contains(pos)).toList(); + List tracks = BlockPos.betweenClosedStream(new BlockPos(1, 4, 2), new BlockPos(4, 4, 2)).toList(); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertSecondsPassed(4); + existing.forEach(pos -> helper.assertBlockPresent(AllBlocks.RAILWAY_CASING.get(), pos)); + filled.forEach(pos -> helper.assertBlockPresent(AllBlocks.ANDESITE_CASING.get(), pos)); + tracks.forEach(pos -> helper.assertBlockPresent(AllBlocks.TRACK.get(), pos)); + helper.assertContainerEmpty(barrelEnd); + }); + } + + @GameTest(template = "roller_paving_and_clearing", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void rollerPavingAndClearing(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(8, 5, 1); + List paved = BlockPos.betweenClosedStream(new BlockPos(1, 2, 1), new BlockPos(4, 2, 1)).toList(); + // block above will be cleared too, but will later be replaced by the contraption's barrel + BlockPos cleared = new BlockPos(2, 3, 1); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertSecondsPassed(9); + paved.forEach(pos -> helper.assertBlockPresent(AllBlocks.ANDESITE_CASING.get(), pos)); + helper.assertBlockPresent(Blocks.AIR, cleared); + }); + } + // FIXME: trains do not enjoy being loaded in structures // https://gist.github.com/TropheusJ/f2d0a7df48360d2e078d0987c115c6ef // @GameTest(template = "train_observer") diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java index efbac27b6..354a02a65 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java @@ -1,55 +1,62 @@ package com.simibubi.create.infrastructure.gametest.tests; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.content.fluids.hosePulley.HosePulleyFluidHandler; +import com.simibubi.create.content.fluids.pipes.valve.FluidValveBlock; import com.simibubi.create.content.kinetics.gauge.SpeedGaugeBlockEntity; import com.simibubi.create.content.kinetics.gauge.StressGaugeBlockEntity; +import com.simibubi.create.content.kinetics.waterwheel.WaterWheelBlockEntity; import com.simibubi.create.infrastructure.gametest.CreateGameTestHelper; import com.simibubi.create.infrastructure.gametest.GameTestGroup; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestAssertException; +import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.RedStoneWireBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.RedstoneSide; +import net.minecraft.world.level.block.LeverBlock; +import net.minecraft.world.level.block.RedstoneLampBlock; import net.minecraft.world.level.material.Fluids; import net.minecraftforge.fluids.FluidAttributes; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.registries.ForgeRegistries; @GameTestGroup(path = "fluids") public class TestFluids { @GameTest(template = "hose_pulley_transfer", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS) public static void hosePulleyTransfer(CreateGameTestHelper helper) { - // there was supposed to be redstone here built in, but it kept popping off, so put it there manually - BlockPos brokenRedstone = new BlockPos(4, 8, 3); - BlockState redstone = Blocks.REDSTONE_WIRE.defaultBlockState() - .setValue(RedStoneWireBlock.NORTH, RedstoneSide.NONE) - .setValue(RedStoneWireBlock.SOUTH, RedstoneSide.NONE) - .setValue(RedStoneWireBlock.EAST, RedstoneSide.UP) - .setValue(RedStoneWireBlock.WEST, RedstoneSide.SIDE) - .setValue(RedStoneWireBlock.POWER, 14); - helper.setBlock(brokenRedstone, redstone); - // pump - BlockPos lever = new BlockPos(6, 9, 3); + BlockPos lever = new BlockPos(7, 7, 5); helper.pullLever(lever); helper.succeedWhen(() -> { helper.assertSecondsPassed(15); // check filled - BlockPos filledLowerCorner = new BlockPos(8, 3, 2); - BlockPos filledUpperCorner = new BlockPos(10, 5, 4); + BlockPos filledLowerCorner = new BlockPos(2, 3, 2); + BlockPos filledUpperCorner = new BlockPos(4, 5, 4); BlockPos.betweenClosed(filledLowerCorner, filledUpperCorner) .forEach(pos -> helper.assertBlockPresent(Blocks.WATER, pos)); // check emptied - BlockPos emptiedLowerCorner = new BlockPos(2, 3, 2); - BlockPos emptiedUpperCorner = new BlockPos(4, 5, 4); + BlockPos emptiedLowerCorner = new BlockPos(8, 3, 2); + BlockPos emptiedUpperCorner = new BlockPos(10, 5, 4); BlockPos.betweenClosed(emptiedLowerCorner, emptiedUpperCorner) .forEach(pos -> helper.assertBlockPresent(Blocks.AIR, pos)); // check nothing left in pulley - BlockPos pulleyPos = new BlockPos(8, 7, 4); + BlockPos pulleyPos = new BlockPos(4, 7, 3); IFluidHandler storage = helper.fluidStorageAt(pulleyPos); if (storage instanceof HosePulleyFluidHandler hose) { IFluidHandler internalTank = hose.getInternalTank(); @@ -62,27 +69,27 @@ public class TestFluids { } @GameTest(template = "in_world_pumping_out") - public static void inWorldPumpingOutput(CreateGameTestHelper helper) { - BlockPos pumpPos = new BlockPos(3, 2, 2); - BlockPos waterPos = pumpPos.west(); - BlockPos basinPos = pumpPos.east(); - helper.flipBlock(pumpPos); + public static void inWorldPumpingOut(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(4, 3, 3); + BlockPos basin = new BlockPos(5, 2, 2); + BlockPos output = new BlockPos(2, 2, 2); + helper.pullLever(lever); helper.succeedWhen(() -> { - helper.assertBlockPresent(Blocks.WATER, waterPos); - helper.assertTankEmpty(basinPos); + helper.assertBlockPresent(Blocks.WATER, output); + helper.assertTankEmpty(basin); }); } @GameTest(template = "in_world_pumping_in") - public static void inWorldPumpingPickup(CreateGameTestHelper helper) { - BlockPos pumpPos = new BlockPos(3, 2, 2); - BlockPos basinPos = pumpPos.east(); - BlockPos waterPos = pumpPos.west(); + public static void inWorldPumpingIn(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(4, 3, 3); + BlockPos basin = new BlockPos(5, 2, 2); + BlockPos water = new BlockPos(2, 2, 2); FluidStack expectedResult = new FluidStack(Fluids.WATER, FluidAttributes.BUCKET_VOLUME); - helper.flipBlock(pumpPos); + helper.pullLever(lever); helper.succeedWhen(() -> { - helper.assertBlockPresent(Blocks.AIR, waterPos); - helper.assertFluidPresent(expectedResult, basinPos); + helper.assertBlockPresent(Blocks.AIR, water); + helper.assertFluidPresent(expectedResult, basin); }); } @@ -147,4 +154,151 @@ public class TestFluids { } }); } + + @GameTest(template = "large_waterwheel", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void largeWaterwheel(CreateGameTestHelper helper) { + BlockPos wheel = new BlockPos(4, 3, 2); + BlockPos leftEnd = new BlockPos(6, 2, 2); + BlockPos rightEnd = new BlockPos(2, 2, 2); + List edges = List.of(new BlockPos(4, 5, 1), new BlockPos(4, 5, 3)); + BlockPos openLever = new BlockPos(3, 8, 1); + BlockPos leftLever = new BlockPos(5, 7, 1); + waterwheel(helper, wheel, 4, 512, leftEnd, rightEnd, edges, openLever, leftLever); + } + + @GameTest(template = "small_waterwheel", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void smallWaterwheel(CreateGameTestHelper helper) { + BlockPos wheel = new BlockPos(3, 2, 2); + BlockPos leftEnd = new BlockPos(4, 2, 2); + BlockPos rightEnd = new BlockPos(2, 2, 2); + List edges = List.of(new BlockPos(3, 3, 1), new BlockPos(3, 3, 3)); + BlockPos openLever = new BlockPos(2, 6, 1); + BlockPos leftLever = new BlockPos(4, 5, 1); + waterwheel(helper, wheel, 8, 128, leftEnd, rightEnd, edges, openLever, leftLever); + } + + private static void waterwheel(CreateGameTestHelper helper, + BlockPos wheel, float expectedRpm, float expectedSU, + BlockPos leftEnd, BlockPos rightEnd, List edges, + BlockPos openLever, BlockPos leftLever) { + BlockPos speedometer = wheel.north(); + BlockPos stressometer = wheel.south(); + helper.pullLever(openLever); + helper.succeedWhen(() -> { + // must always be true + edges.forEach(pos -> helper.assertBlockNotPresent(Blocks.WATER, pos)); + helper.assertBlockPresent(Blocks.WATER, rightEnd); + // first step: expect water on left end while flow is allowed + if (!helper.getBlockState(leftLever).getValue(LeverBlock.POWERED)) { + helper.assertBlockPresent(Blocks.WATER, leftEnd); + // water is present. both sides should cancel. + helper.assertSpeedometerSpeed(speedometer, 0); + helper.assertStressometerCapacity(stressometer, 0); + // success, pull the lever, enter step 2 + helper.powerLever(leftLever); + helper.fail("Entering step 2"); + } else { + // lever is pulled, flow should stop + helper.assertBlockNotPresent(Blocks.WATER, leftEnd); + // 1-sided flow, should be spinning + helper.assertSpeedometerSpeed(speedometer, expectedRpm); + helper.assertStressometerCapacity(stressometer, expectedSU); + } + }); + } + + @GameTest(template = "waterwheel_materials", timeoutTicks = CreateGameTestHelper.FIFTEEN_SECONDS) + public static void waterwheelMaterials(CreateGameTestHelper helper) { + List planks = ForgeRegistries.BLOCKS.tags().getTag(BlockTags.PLANKS).stream() + .map(ItemLike::asItem).collect(Collectors.toCollection(ArrayList::new)); + List chests = List.of(new BlockPos(6, 4, 2), new BlockPos(6, 4, 3)); + List deployers = chests.stream().map(pos -> pos.below(2)).toList(); + helper.runAfterDelay(3, () -> chests.forEach(chest -> + planks.forEach(plank -> ItemHandlerHelper.insertItem(helper.itemStorageAt(chest), new ItemStack(plank), false)) + )); + + BlockPos smallWheel = new BlockPos(4, 2, 2); + BlockPos largeWheel = new BlockPos(3, 3, 3); + BlockPos lever = new BlockPos(5, 3, 1); + helper.pullLever(lever); + + helper.succeedWhen(() -> { + Item plank = planks.get(0); + if (!(plank instanceof BlockItem blockItem)) + throw new GameTestAssertException(ForgeRegistries.ITEMS.getKey(plank) + " is not a BlockItem"); + Block block = blockItem.getBlock(); + + WaterWheelBlockEntity smallWheelBe = helper.getBlockEntity(AllBlockEntityTypes.WATER_WHEEL.get(), smallWheel); + if (!smallWheelBe.material.is(block)) + helper.fail("Small waterwheel has not consumed " + ForgeRegistries.ITEMS.getKey(plank)); + + WaterWheelBlockEntity largeWheelBe = helper.getBlockEntity(AllBlockEntityTypes.LARGE_WATER_WHEEL.get(), largeWheel); + if (!largeWheelBe.material.is(block)) + helper.fail("Large waterwheel has not consumed " + ForgeRegistries.ITEMS.getKey(plank)); + + // next item + planks.remove(0); + deployers.forEach(pos -> { + IItemHandler handler = helper.itemStorageAt(pos); + for (int i = 0; i < handler.getSlots(); i++) { + handler.extractItem(i, Integer.MAX_VALUE, false); + } + }); + if (!planks.isEmpty()) + helper.fail("Not all planks have been consumed"); + }); + } + + @GameTest(template = "smart_observer_pipes") + public static void smartObserverPipes(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(3, 3, 1); + BlockPos output = new BlockPos(3, 4, 4); + BlockPos tankOutput = new BlockPos(1, 2, 4); + FluidStack expected = new FluidStack(Fluids.WATER, 2 * FluidAttributes.BUCKET_VOLUME); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertFluidPresent(expected, tankOutput); + helper.assertBlockPresent(Blocks.DIAMOND_BLOCK, output); + }); + } + + @GameTest(template = "threshold_switch", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS) + public static void thresholdSwitch(CreateGameTestHelper helper) { + BlockPos leftHandle = new BlockPos(4, 2, 4); + BlockPos leftValve = new BlockPos(4, 2, 3); + BlockPos leftTank = new BlockPos(5, 2, 3); + + BlockPos rightHandle = new BlockPos(2, 2, 4); + BlockPos rightValve = new BlockPos(2, 2, 3); + BlockPos rightTank = new BlockPos(1, 2, 3); + + BlockPos drainHandle = new BlockPos(3, 3, 2); + BlockPos drainValve = new BlockPos(3, 3, 1); + BlockPos lamp = new BlockPos(1, 3, 1); + BlockPos tank = new BlockPos(2, 2, 1); + helper.succeedWhen(() -> { + if (!helper.getBlockState(leftValve).getValue(FluidValveBlock.ENABLED)) { // step 1 + helper.getBlockEntity(AllBlockEntityTypes.VALVE_HANDLE.get(), leftHandle) + .activate(false); // open the valve, fill 4 buckets + helper.fail("Entering step 2"); + } else if (!helper.getBlockState(rightValve).getValue(FluidValveBlock.ENABLED)) { // step 2 + helper.assertFluidPresent(FluidStack.EMPTY, leftTank); // wait for left tank to drain + helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, false); // should not be on yet + helper.getBlockEntity(AllBlockEntityTypes.VALVE_HANDLE.get(), rightHandle) + .activate(false); // fill another 4 buckets + helper.fail("Entering step 3"); + } else if (!helper.getBlockState(drainValve).getValue(FluidValveBlock.ENABLED)) { // step 3 + helper.assertFluidPresent(FluidStack.EMPTY, rightTank); // wait for right tank to drain + // 16 buckets inserted. tank full, lamp on. + helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, true); + // drain what's filled so far + helper.getBlockEntity(AllBlockEntityTypes.VALVE_HANDLE.get(), drainHandle) + .activate(false); // drain all 8 buckets + helper.fail("Entering step 4"); + } else { + helper.assertTankEmpty(tank); // wait for it to empty + helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, false); // should be off now + } + }); + } } diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestItems.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestItems.java index 5fdf5e0fa..0ab535a93 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestItems.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestItems.java @@ -34,6 +34,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.RedstoneLampBlock; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.registries.ForgeRegistries; @GameTestGroup(path = "items") public class TestItems { @@ -229,8 +230,35 @@ public class TestItems { }); } - @GameTest(template = "content_observer_counting") - public static void contentObserverCounting(CreateGameTestHelper helper) { + @GameTest(template = "smart_observer_belt_and_funnel", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void smartObserverBeltAndFunnel(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(6, 3, 2); + List targets = List.of( + new BlockPos(5, 2, 1), // belt + new BlockPos(2, 4, 6) // funnel + ); + List overflows = List.of( + new BlockPos(6, 2, 1), // belt + new BlockPos(1, 3, 6) // funnel + ); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertSecondsPassed(9); + targets.forEach(pos -> helper.assertBlockPresent(Blocks.DIAMOND_BLOCK, pos)); + overflows.forEach(pos -> helper.assertBlockPresent(Blocks.AIR, pos)); + }); + } + + @GameTest(template = "smart_observer_chutes") + public static void smartObserverChutes(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(1, 5, 2); + BlockPos output = new BlockPos(1, 5, 3); + helper.pullLever(lever); + helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.DIAMOND_BLOCK, output)); + } + + @GameTest(template = "smart_observer_counting") + public static void smartObserverCounting(CreateGameTestHelper helper) { BlockPos chest = new BlockPos(3, 2, 1); long totalChestItems = helper.getTotalItems(chest); BlockPos chestNixiePos = new BlockPos(2, 3, 1); @@ -253,6 +281,26 @@ public class TestItems { }); } + @GameTest(template = "smart_observer_filtered_storage") + public static void smartObserverFilteredStorage(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(2, 3, 1); + BlockPos leftLamp = new BlockPos(3, 2, 3); + BlockPos rightLamp = new BlockPos(1, 2, 3); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertBlockProperty(leftLamp, RedstoneLampBlock.LIT, true); + helper.assertBlockProperty(rightLamp, RedstoneLampBlock.LIT, false); + }); + } + + @GameTest(template = "smart_observer_storage") + public static void smartObserverStorage(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(1, 3, 2); + BlockPos lamp = new BlockPos(1, 2, 3); + helper.pullLever(lever); + helper.succeedWhen(() -> helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, true)); + } + @GameTest(template = "depot_display", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) public static void depotDisplay(CreateGameTestHelper helper) { BlockPos displayPos = new BlockPos(5, 3, 1); @@ -275,7 +323,7 @@ public class TestItems { DepotBlockEntity depot = depots.get(i); ItemStack item = depot.getHeldItem(); - String name = Registry.ITEM.getKey(item.getItem()).getPath(); + String name = ForgeRegistries.ITEMS.getKey(item.getItem()).getPath(); if (!name.equals(text)) helper.fail("Text mismatch: wanted [" + name + "], got: " + text); @@ -283,8 +331,8 @@ public class TestItems { }); } - @GameTest(template = "stockpile_switch") - public static void stockpileSwitch(CreateGameTestHelper helper) { + @GameTest(template = "threshold_switch") + public static void thresholdSwitch(CreateGameTestHelper helper) { BlockPos chest = new BlockPos(1, 2, 1); BlockPos lamp = new BlockPos(2, 3, 1); helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, false); diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestMisc.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestMisc.java index a858508d2..2079c9492 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestMisc.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestMisc.java @@ -3,6 +3,7 @@ package com.simibubi.create.infrastructure.gametest.tests; import static com.simibubi.create.infrastructure.gametest.CreateGameTestHelper.FIFTEEN_SECONDS; import com.simibubi.create.AllBlockEntityTypes; +import com.simibubi.create.content.redstone.thresholdSwitch.ThresholdSwitchBlockEntity; import com.simibubi.create.content.schematics.SchematicExport; import com.simibubi.create.content.schematics.SchematicItem; import com.simibubi.create.content.schematics.cannon.SchematicannonBlockEntity; @@ -16,10 +17,14 @@ import net.minecraft.nbt.NbtUtils; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.animal.Sheep; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.monster.Zombie; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RedstoneLampBlock; @GameTestGroup(path = "misc") public class TestMisc { @@ -63,4 +68,47 @@ public class TestMisc { helper.assertItemEntityPresent(Items.WHITE_WOOL, sheepPos, 2); }); } + + @GameTest(template = "smart_observer_blocks") + public static void smartObserverBlocks(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(2, 2, 1); + BlockPos leftLamp = new BlockPos(3, 4, 3); + BlockPos rightLamp = new BlockPos(1, 4, 3); + helper.pullLever(lever); + helper.succeedWhen(() -> { + helper.assertBlockProperty(leftLamp, RedstoneLampBlock.LIT, true); + helper.assertBlockProperty(rightLamp, RedstoneLampBlock.LIT, false); + }); + } + + @GameTest(template = "threshold_switch_pulley") + public static void thresholdSwitchPulley(CreateGameTestHelper helper) { + BlockPos lever = new BlockPos(3, 7, 1); + BlockPos switchPos = new BlockPos(1, 6, 1); + helper.pullLever(lever); + helper.succeedWhen(() -> { + ThresholdSwitchBlockEntity switchBe = helper.getBlockEntity(AllBlockEntityTypes.THRESHOLD_SWITCH.get(), switchPos); + float level = switchBe.getStockLevel(); + if (level < 0 || level > 1) + helper.fail("Invalid level: " + level); + }); + } + + @GameTest(template = "netherite_backtank", timeoutTicks = CreateGameTestHelper.TEN_SECONDS) + public static void netheriteBacktank(CreateGameTestHelper helper) { + BlockPos lava = new BlockPos(2, 2, 3); + BlockPos zombieSpawn = lava.above(2); + BlockPos armorStandPos = new BlockPos(2, 2, 1); + helper.runAtTickTime(5, () -> { + Zombie zombie = helper.spawn(EntityType.ZOMBIE, zombieSpawn); + ArmorStand armorStand = helper.getFirstEntity(EntityType.ARMOR_STAND, armorStandPos); + for (EquipmentSlot slot : EquipmentSlot.values()) { + zombie.setItemSlot(slot, armorStand.getItemBySlot(slot).copy()); + } + }); + helper.succeedWhen(() -> { + helper.assertSecondsPassed(9); + helper.assertEntityPresent(EntityType.ZOMBIE, lava); + }); + } } diff --git a/src/main/resources/data/create/structures/gametest/contraptions/controls.nbt b/src/main/resources/data/create/structures/gametest/contraptions/controls.nbt new file mode 100644 index 0000000000000000000000000000000000000000..c1b8456f9fdef17da154fc8bfaeb1933ec5af297 GIT binary patch literal 1579 zcmaLSeLNEg9LI4lj`F;c$8k;>PPCG;Ve$|Pg*?ryY-r--@es!<4^f^v=CK#^TnNL> zv&qmrEb}}i&CEldEp08u?p}BI*S)TO|9)Pt-|PKVO*t&^_wirl+XlULK6bC(cOwdP z1z&9LPVnk41tvIG>6h6#Sv}StD#Ha@MH(BN4TO<;IUKtQN!gCZn`x%W{4p`L221lb zC9FWHQJ-KUixOg-zS8F+ezSbw>es?B+WuCr_tSoORz7Lh)LGG+X)lBIXEqEfktfn4 z5*Trele(^)z4k$W;5DQ18c+W;(Y-pQ$?K=fy#W zeh5NXgQ_^b;5ox<*3+sc_2GXMw%{AEOJfsh3@l<3w%0VlKls!kpYoiZRa*O;(-Jxj zR@a<2dX#}V=z<8!RN%FnifIV-BbKR-aaA}}HAtc92k&f@KPWF+w9@Rfm1xp{gVgUO z%Ev9B7axQsODq8tht%|N7Cu@@t71Am-)u3u-)xxg{B-?JBOd(*LTO(9hdI z{!9ONQ`mSU@Pu2FBcWaidLboGsb_w|uDRBx=MD5>RW2$u4(lR68Gu7IMp1?B3J1K8 zO$Nv=RxPf%_f3k5s9;v$wJ04b62@6nZ0)Dk$+39%*~G>Bjvyiim%T zylr~PTd3}2Qimia@IwO}KwgMBIcVu|RVtuPoFuXoZg3;k9YTIR#rVp{WvjfOt^p5g zEA&--+MI8I&W_br#q@X5vTKi<7)!^D6Zq-K_Ldo=bKw!8)RZAOkYpjsb~;TfK}4aSN}tOtKW9f$uC z8T4Bd{aq)U9j_SXYRvQDRuSAJ-X1aHG zp-!ir6A>M|2;GQkn8FLVdM;h{L7RNFNplV(9j3$5&p|A=NXu;J=7-V*o$$eKw{*#D z!^j1aqUR0hn+=il@l}SWJsCo^W{e54P)3J#WlG3=sTYv+3f8)*D5ewtvdCztjW#ut zsQei+EbBA_PaF0dT`enC%vjk$Ud)zWQ5yVkLhBq2Z0U}KDw9(Q+_v=;k($lkJX5fa zr${tz8PPhplvMx#=RsMg-sY}l`H+^Hp2U4o%(710LqwO-rS+BZH8;XE^c2BV#%8q4 z*8Sm?wQfz9NJ7!x}ZYhD@woulTBOod!EVD~nU+KSiE;BJK)2G>858gc%( eFel9&kfMLhaWI(_W6Y7ze1Uch3QSTK5cm@rF%)?K literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/contraptions/elevator.nbt b/src/main/resources/data/create/structures/gametest/contraptions/elevator.nbt new file mode 100644 index 0000000000000000000000000000000000000000..6e962e5ca72b5c684124e279a9f6ff9fbf1bd5bc GIT binary patch literal 1109 zcmV-b1giTViwFP!00000|IL@bj~hi4$7j7e{(YbzL5dWJj)FE6Y0igFj*N1-BhFm_ z2|07S>8JNDh)da@@Z zgr(K&+TZuy=e>C|o^2!4LS7txN?nAIz8Y|K;leG{q+Eq6q;VUbYi;E6L)Aj{9ic+O z>$$%40&0T&|ii8I`?m&uZ>gU;C=k3k2i(c6iQW4e{ zf(2s6^_Kv4M<}8pum&?m7YumVhL@b+Fg;l*vzRmU*u!{+(F*{{%{-ayztB4`FGdeKm96t z{Nj`TPq#GeArt;Xz%Ui|(gHzbpokB=c9tg#%we;W}~q&8;yj?jg8r8Y|KVuW3E?VHX0kV(b$-c#>Q+kFI$67A(m6pV@`p& zwnlT-kGn?WTKsqwVtFO8)@f|9aD`a7LM&V%R?S>zG+v3OUC`hXO{bu#m*ifI)hOAY zi?N#dS)`5XgODjIv%l_*X~?NB$wb{CaM(b&59bW!fSYb6+nVYxz(FrFxR(Fd`+_S; zB7LA3r}u)za8iMTgg(3kj)NL|Vn$_p>+MH(MOo(+Gil|N7~xSwY4F}$XVSP$)lA5T z7&ZGOB7Ue&hI8$}8^O_6yTE8;D+p*{ejdzqrE0?fe%oC9eZP-UeWWCf&N30YG3PN5OnIj)N~h8)h?gzZiyG1cWlCIyt*;oTXW>$IECcHaE_ z*FUd2PX}*LCeQzP1ckbja31m_ltH|aLr_MK32=;~gfV&ox_bm-fDV$$;yjyQSo1i? zJlpr33YfamHq2_X&`D<@t8~vk2UYWih8>AS`tXY5e5e~4#*bj^!}#%HvG^91Ri=6eYJAZkR)W)Xy%2vTFqsn`&^T1|ve9O=O07iGI4aY#98_YSu@sAQ97>fCwscd1Sg07d276xQw{5k>sO2+Yx;P!eV>-?@wl%hx!)xP%3td@Q bk9zamCW;(T83Pflp|8j z6NiMX@$QSg%6Mj(os9`qY8ndu03`kb3M!g|hMIzgj*j+9y5r66j$`jS8y`AH(Zu%m zecxwh-@KW00oK4z<8ROd0Q1`_fBiM+pwO|7G40ZOrwg7K>NRM6CUq=DtJE)jIk0gk z&ND7hu^~rr!P||M^N9nUBk@SdOhhK{fs&Kl=jGpv@h@K$qX+LCOQjj#lafbrD$Iuj z_Ec(SAOd>p+Mob74jAYAajKaJv5xm%1bVZ;hwzwUq=NAPZ_ zkZF9FW(lgnh-bJ4?upDTdL?D=)#A7g{TmUb`TgOLF25z zV>L}YR@1~|HBCIT%2+|u#A7v0JXX`hGh4?MG)+8K)5K#nO+5YzJXX`hV>L}YR@1~2 ztiWS6O*~f9#A7u#)w5s8<$&#j!J)=UnuiswV{;M<3>7=o55qC0ddUhn6*sqZ^I+>l zF_E64TI_VSBwJTW#xkKEbxoG}xqGuL!AU%!keSpq*^LA!a*!Fs#6xC z1MOouMTHS9S!cetE?F$MGCjYtSf+<zf+`b86ro6Mt)I6)c`$p>bY7|3^6di!I z2Xenq-kG16h^T-?op}4kMW_jeOey5o6#4w_3hKM7_ttlpza(+1OdLSaPmnCYTt$MT zsmLMxKOgCe`&q?zkjW1~2rSXfR%P()~^Q@i!$r`|| z-6+DyetmFhyszc2t#i3EH|#n~s*WYNK?Qe>e%cA+s5P2~N{z;U!X^m!N+ZQ$K4lk! zkfr8fb)kE#B%KuICQn*Zci>OyrraT)vif|jGVDN$zBDm`ppqSfb)GxfIG$5=RXo?H zlvi;%8ks^qWjvkp@~3nbl9Rqj5LyO_N^psDab{MW7l8;Eh1p3Z@XV%uDmMDe;%?rd z*BQB4LMHUNC?~v(rR*WsSmX=y7Et{Bx?p`8wrwT8`tW`Bb)158F|%6SYpbQVMVJrq gISz`lrsCgcnmtFA(!Xugr+$F{0M^&bdwUK50NK+t(*OVf literal 1436 zcmYk+c{tR090%~^2?u{1F3}5>3*5{W=5Ni^!(ezRC?t$q=f!Sq3O~ift<$oGl25!m*kh_ zu0cyvn)%%hx9rAhFJa0EOkDGF-rsKtiQ{|6=G_c^KG!PC>M+WRmVUMxrw-4t_^(a| zuap0Jxv`@4&;d)}3_%`;(K6?6I0Pcc%<|#J2dP3& zvAGrfxD*+*jTC=k!`I)$i9g(H;Keh9_TU)9h}cFKgwGinDwz$C|XeF;GnP9tlgS-5k(K^+H5EMX1)bHWl!YC9bC zUjdV!*=c>Bz`*}2sIe41Y7GVoGFK}{7?;TQKtWO306}V|7l7^g1_FRd%$IM~A6A^2uA~ z`u|!T#M|?D|07>4uB3%|1%SCdBN`NjC9nf++cP9lF2-5jxSP2d(<1{OD_f$0V=wle z=AE)|th0>O1*bClD8lo0H8C{5zLbh^hlXfyfLhxLrxxYBsSA~=i0C8C2(8IG))`YZ zr^tRvr_Ab-Z6PDYmZ$xCL`s;lOEx#JBtg$WbF79BJ99mYhBmKei&CA(p3@sWB7ayb zONut=*Udi=UaNODlyBU8W-{DopHW}z>NX+AxVWv;G)E0$IwS=1l);vUP56v825;m? z6_1W=V$j{L{Ot!^HH1cArTV)vFs{ZjQ8XB6%S7absP&u%P*UFkp_&U8$6-RA#ycI- zpl&Cmgs3Eo%jMu1m!XCd2-thi1bsIt$FIw^u!02x?lcucM8QXnT{#}RjGwkBNLP(LTM#0Fyi&nP_mV?dg?~}GOfMrp5lA<_P^981N*kSq zhrHOX-FT8|j`aehd}wpJx^?u%xeLorSfBIai)jWO7o|orb@pA4f|ru-dZl3+D?4sQ Z7J7=Ei_C{#{wCU3@=n*T1Cg`<;2)&xug?Gg diff --git a/src/main/resources/data/create/structures/gametest/contraptions/roller_filling.nbt b/src/main/resources/data/create/structures/gametest/contraptions/roller_filling.nbt new file mode 100644 index 0000000000000000000000000000000000000000..862b2620517b04ab5d029ea4b05c7521e3d248c9 GIT binary patch literal 1120 zcmb2|=3oGW|5s=D=HE7xI4-~U<|pr6Jh!`Qnq$lECb~ANcuY`|oyJ-$y=HgCmLnD} z8G#F4Sa4Ron0{mF^mp8E_C;6UUVfwYxa{`WJ?_)w)`z7=MO5A3k?2_?|Nqa#Ng776 z4SYXl?szK4@Z>&U-R&Q|Rsx@na41(?JDR+lJI=^RHlucFPk`s@C)xIVU-~DnUg@(V zP5I=Z)=zei@||pv|J`HtNu}ps->?1}|37ew+#T!CTLzU2ru1CZX>3jW z(QBkLo#VrWrCy9*`e#gOS2EV=tZ1xSt`%+a@mY7%Eo;qF=I8gtPtnWbIQ#swK#btw z_KDBVd|CK z>D*{3oLs!D`W{pUs@{+5%_@RK*P6&zmOLWllyMExAe3lHo|TE z%F{Yi-Z&IAa2tKreD;-XVpz~T!ZFIgtm0y5Hen%CY$)GJJFbUSrhb(^gqXTOQHIlr4v zwqEwK*RK?QroLumX`b@=2Sfd9r{68JXI`4l*ut)pI?=$qUtIAYL%B?)3q$np!k(JS zUoTweJ(WBD`Dgs@&-V2-5)Ro3Cg(RLm96f3+3mE`pLs>kB~yQoQ1cZpp51i4bUgLa z3{|)LJi!wmm@Q6YX!1&W9naUWbaU&4Finw@D?9Z*sL8BY*;lapS&jAl^Yw>6Np9lb z_=m4Q{@UK%uZ#?*$FXt*t*>$#i ziyh8=@rb=1^B^mXe`eV!74x>|pB?O3{5X z?L@}PW64)I&doei(eY4R=jhx&TMro+U-hhH)0jQaadBixWbc>rlJ8FZm3(*JIV9M4 zo#Acss;-N=HZ!wXINyEMpVlb<$C%%T}{di@0)vtFf zJJpWPT^1nw%T;mRj}0?cU%T?wQ-80{)^(CScx;bU zv}~dMdDiD?GdEkEUbEJ3@tfs7(jO{>)=Pi9wR}NjPPWpch;@6Xm3y3%7s!1l6vKDt j?WUlw5098%Q8a$j^IE6A(BZx4AKp7tlHR2VGcW)ErRX?| literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/contraptions/roller_paving_and_clearing.nbt b/src/main/resources/data/create/structures/gametest/contraptions/roller_paving_and_clearing.nbt new file mode 100644 index 0000000000000000000000000000000000000000..4ee3f97877dffeb6bd7255dac5b0e20c62a1888e GIT binary patch literal 796 zcmV+%1LOQ3iwFP!00000|E-qMZqqOnhEEbZP1+460TYkG^>)KFGzl&yq{0N;RBm$G zS##saao2TMdk${kO?W5nkdTVqCS8{$n?)BXO56PB`;U+9<8}dJAO*k1008XWHFsz~ z#}Hwrx#n1ayo`YAVdC1Fi@bVqJ zd0X>$}OnZjc>nBPIamK_HR}-_dA{X}p*6ry96vxMvUhh;G7o0H4dmcs5)ni_ri`sBv4{P*c% z@%Pu_&(A{&VP7jOigIb!YI?Iud8aC%)KWF<(|WqJmM$#k-jZIHq%q61nh$cGahWv~ zpuS4N|17`*gIy)GTPBoWj(H@%TH}m~_Qv+P!=b=J%M5L5AC=5e+5ePAJ7Ng}nK2QmljEo6?zSRehU_OiK>q5%}iEG$nn^a=!l786E(vPRBNxSGplfanwwbj zdsdRysQZns9Rc+cE+7072dkdb5=l}y%OcogSG*W7A+zyd#FZINEtUPdizYGYa5lbI z`g*mA<$^a~bMAJF*6^ytBxg!PfLYpB?#9*Zh7Bh?O-1WOZb7ySUFmJWW#O*ZLG?{$ aPMUX2volnMJpiEh1O5R5Vs3YI4FCYJ%X;Vl literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/fluids/hose_pulley_transfer.nbt b/src/main/resources/data/create/structures/gametest/fluids/hose_pulley_transfer.nbt index f42e51fe69c5b98e3404ae8c7c137e096db2b8c7..5a21ce41116af67f39800f0e4737ba0f029fa034 100644 GIT binary patch literal 1710 zcmZvac|6o<9LIlVgd91Gmqw*^MbRY5G@_WWuBiD(rStd4`}KQ0&*$^~e4p?0?2Q)!K7DI#X)g+?#xgOg z_X1w+A<9Vndo4%pMAx>3UH03fDL)gRU@iSKnrnK3r<<;%JG+eLprI{y+5V-hl4o{(T z>vQauLHz2sW}YaF|3`kYANg@V@~`at{#38f1_l%F(h?5aV*diN02P2h-rFdpp$$KR-AU|=szu{OZy3S>HrIb|V;T2A0cDG{;& zg>4LeL#?=x&{LwlBCZuq{9t^n$0PFR+#sO^4M_Whwj@EC4<^tK#af9jT$3x5t@p3l z7c+%`(=eBwn&LcN!&xSwaOu%>gN{)QjJL>L2#)cHw>AO6gxhVfRshd5{BeG9gQvlG z9U5{IMg->(?3;Gq`3Yle5+6#GAPuhB_ewV^e3PlD-k8f=wT%qSr;G@)5i6%i`=~=W zOo~@M34{U>Wb2p4aYdVypUdYXZvp~Z6&6ITH=_#TRd)k2O;u~0`Zl|!~ z)fm}P;x|v!EXBQ+?7rtSaMU3VZEK>#?(srWIibmPcBSN`%`f-$+;GY2<~LPjUK|-d zzC=;i@U>TN8NcgTkWMdjT0)jub+oPyo*X0Z)qtL`|2sWXa^bKm+odvp#>ZSLu0Jaq zR?@~r%pGsmWwVoN!}mg`7S$O&eR}18%nWFhmoqDHsH}L@ZPj4?VZDEf%E}d%if2X0 zLlqb!fkL#v`D6N)HxF6{5=--fi@3f7(w^Mf-tgMa{IYHsk)_=mr($lRq{Zx?tNl$w zEWf*m?w7Y1LG^8!JQz~0O@d!WbOo?o(d5GV1rgb4h2H5!1)^9|`s<#R;nTDFjx+Fw zoJVHXgG@b5-E-d6iQz~semCM%D&*-=s_`_BFST8ItanWR>C zb=8GC5-C*Qoz?JClQ9!UpFtQ@nPkV|I-j(%GAh|z?^+K#My5u3seDOTfO&n=e^W|C zOE)SqN?B$yvb6NAJ|*&9s9zo?tqU2jpD`%zT);kt@sMSqI$J+5u5t*rYI!de#*ycC zvHZ>-eC=0lDxTNzVrF*DQ6y-kYuq)E<%>Yx?cQJ#)x^4l0Iu h(^pn!3vZK?RWQDJFbxqRHA*T9{PgCj3wMqH!2dEWV4?s3 literal 3467 zcma)8dsLHG5_i>w)uYfO?SfFCwKl1hB>{PefpA)7Ei2+$K@8z3L_|O;4@n3S6)UR* zt%oI0Ua_)8Em#&KfdC<;5h6_>Cyl&>0D^$L5<-CRX7BxyPzC?k{Bd*7d~<&@^P9Od z*D~!d8<5{hOTO$D(Z~Go?eW-1@!_wCTR-rs*~Hv2;$7&K>H7nF*N+>|qizj(oURYK zJ+|+AcHimBm!<`m!+Nh0uRYx~p?CR^aPLP?-S!tKC&|wEm2#u6zxbP5VH~4E{bGfy zRw|P0stN3j0hQ!F2`zkF&m?yAxsnLc=+UfecK7(4{pnQ`%rNYxNchF2~k~ zyQ@wY`6`3ez(EP#&}dxs<{t;LaIcDgC_eSZh=EayIqD}sfA>2e^EaGINZbiHNn zJJcZB3?=(4^Uc3!H08N89G&z%yOMv35w6`<0C%i}kyRCDn(APXMBMyd1L@ZJO)PnV zqn4g)%cmGNz#ZB&V8?=D)z?U_w{+|{X0QC$~$?76<=?~i&LMZwKN)GfHAlpZX zTeT=d9o5LICMkD>apflKZ|R>Evt zGF)`QLQoOB14!T?zDF?L!ETVOGAou2WJ%MeBmI=1s>+g zk1ipEGDr@x3etB+)hHM`^~Czp7v^*ty3+;Aok5CZLcIlN$c+Lq zSXKpf;9{e`gv3@1Jx7+<9``l43KVow6DR@;uZxwi9jT;I#Iyq3Q!Szvw!Zj1$jrA; zzUL_n`D3{*yGQ9R7)=gr)E9wy7fgum<~0;#*{C7iK&GwZ5HUzn0pJ(>Ya3*{%62ed zH)0Y5D%QFp*A(?hXct)qyN@8D#HW@GX(~?yn;SR)${b1V7-gp>-XWyM=o~(ThOG^; zrl}_IjixCQP(;)KO_pPOH1q>(E8sm$M4FEepwtN=FZ}S&}K4s-7*Q zmX@|dJly`B+YYy&6$Kd6XNq*Zf6XKR&GEZ?xJYnh26JR)*>oW@euf`@usBR;ll-j> zvssvuAyrl`hB^veO9Zv@_QNS1XR#0G2^Z|}k6v~P3f&3DgAWo5o$(Lx5A}oWCDubi zMRInS3@4CE-7}RhnyM6*=1szggI|kThWe~jQv6(2-|4GdN?o*N|8xgWoyT&`9?mMf z5aDFXSBBdsc_l1%PsKS{WrfVP4VYM13ntu81h(B@UQWv~RQqH`6c~24lkr_EyaL0a zCe_D02TS%oiN%Hx^u9>L%N=a}=b!krmUlJITUw!B442f*RwbyD=R76>0isaj474D>rt6GJkaP zEUR705d4tLrHt>NYUOyOt6H9)J!PA#U&8DBQ`*aOfkdyi)X`%@?)q)c-`!;AHQ|#P zFNUIWe^0j03g4e2#7g|Gmz}uokQE&8xMg>9HCpZjnAjhPK5xcKwb4I<-je6JLN z6zml{O{Yk=b~5;?ypwmUM_DVyn2Wn7BR(n1zj4zbq-|TtYpjG~XHtD8Xh;$?mU9JM zWBX09Yu-P9$jc)12H`OgO*wJ{NB7_N`mL3^7qsr5cY=y^f?E4x?Nn!pbTsYpj`F2)9byP{K1w0?2IA@pylt_2cKYc zS!bwRCnDRb19Y#lc3#A3VBy}cU2s<3fr?LtyQ6}}x=~<&Up}Lig;MuxAQ&e=-;cQo zrjRFW&U*%tDdbcSn$UetrB#N1YKj61G5}J6j;`ws3+Q(A$qD3J3jZg9BJ(SOtbS+jklf6jwk|5{O z&S8t~*V8_(8ryV~7C}6bqMW^((|r9lTg|-X!m2YY==i7<=Wl&JFWWP0DRGyT>F0*5 znw)%x&mVmdeEhDNTYL3qP2~BBVA=2K5<+)ie8+RfkukIMD86PZ7BzwYnyLNIZ;U7{*-)gRJjiJ~g zbJ{d)M?k6C7q%)R7nP%^r#C@DA$KCMr|`KBBDE~5&ztd|2#}3iTm1;cqw;D O-BQKF#NUdhQD{EPyryjb7L0kOK@W&a> zP@4dZAS(C^3<1FX4(c%;!4Px94aWl9X9WFoA=AquvHyHEf>6nVjMkqJ;=(Y4wMCmI z=%DF!(1aZ{kw+7BU?z=^nKV9T()gH3<74i1U?z=^nKV9T()gH3<6~auql3oBOd20E zX?)D2@i9jom`USfCXJ7oG(Kk1_?Y7k%%t%#lg7tP8Xq%he9Zk0%%t%#lg7tP8Xq%h z{;6*SjkSW7x(FJ~yEI`3&E{IYmb1}o_z59_Fh4gj#1n-$bGsNre7ww2JCpste|!D@ z&wIc8@!{KN@814?JAnvUVXRfO#KI&nI98ZH%4hbkGp0Gq%@E?bDESON$#{;bW^;2F zGi83ZfP<4#2%ay32!;ntv6S1#RnES-h@k%tA%aj=|a|?OmHWEBt@drQH zao>(V+VKk*LUhPdgy{Q6lT-U-2tB+0XQ@WJ%ext-7cA!~6H`@Ys-?=Ck&9ZQvhsPS z_F3>u37lTq9U7kQaJd=ixOUkHVD0wv+U-KHqL^0Un<}obC4)`YeuMR|H!gC6Cuo*Z zU&hyQK6>MvL=$_3I~Ni3j_k&Q`w;xPA47j)G#0B1e8sd^Go_+5=|y4{veYToNr)Rs z__D1NHG?o%2Vs0%8daJjgIRG6H5x%&J)#4U9OWHU{X;Gc>Kgm{)G@9Dh!5PEa`kfi zAurguz}ZB}YW2EiYnD@%uU=Vt3B-@_3I&Dq-bi$851Se>yMr3fy|A4 z4eC+{T@JtV`(L04;npg;>xuiLIxYVPtc_xI3UAfLbIuB$2M~RyPGw+Ul#j1a&xKsp zM{n=YEw%Nq<#KG6{P16vd<=UJW;2}8^x;caraRj}2;)hrr4SUfpSJgwNh9?R+|8;V z*`I-80*yg;ToT)^pRf$!Q|d5MDb&-{#cBaGyAl7YExLAaqNP>3ZyGjD!X+DmuiGq^ z)~{|_M00mZ*z`(io5euMC2HH;Bcn?kLf_81^{Fr4@afjO;d5NAB;;aYJ;ie-icMEl zt0z~x?xQXqBlesZ5In!;TE}j&yg(a=E?z<=%x;%EFQJC(X5el(+P*oO=Ih|h@~ literal 971 zcmV;+12p^}iwFP!00000|LvE{ZsbH1hL5kY-4ihj;5k@F!-$#8M2a+;M06A`n^3V& zI;E5^xXRNrtg>d!0*M8zcnDSzI}ne-ORx&3>@Fv1>{5vp3oKdg?)vN0=Tm>3s?GpX z2qpgp0|3x>Ot(*WltO});)j?jg5Jn4+o8n*=TIcMq^_( z8XI%yz-%-&W}~q&8;ymfEL+U}jK*`&_zs%DK@(au zo&&Sd*qFTuW^YDgWA+@FeFtWLf_azLs|ccHgIN4} z1F(STXkB5ehy3`r?|%OCm#5$U@YnON|M>m;#~Fmkq@wNaxJ42RN1>(%QY76a&4dlAG# z%f23f24AC86!)mX>Sa~Xr&HE)R;dJ{3(@fc-Ya>9dCM-;9+r)IM}c;ZJ0W};9dhXHlOVA58f$)Ik2;pZhmnV9y zK1_)Y)~6)_pSV8ETcnilm6%_$isww6HC@?c^n!KQ<~TDI`Ib}< z&>w7|B`+I+XIGS3#q(|E(3SbV$)g#K&E%nj$&F^p^YOXN@4dvhdiQsd?D)fCXLC1?=#EietC)iWqFBUx3BG{`QX}G-2+%G^R^bE zPuA`2?N1NisjOAq&fwm!EITtATV;tHDvQzBjwwcCJ6c5(%yXJ;=ADKKR0_5I6WV}Y zvl8L`O@i{eX|O%Z_1v$(wBGa6N-!zMhOI(HRqwd1DS-YDOKud})8KtH6=+Sv{iDaC z|5enh6&7S>CL|^hU(nR+tyiBK#!xexp;N?=z2y?Tw|89Q>#x?ANX%PNMuehXS7WaS tD`mEeK5mR%%x>5}_!~SM97(=?!4x~CLk}Gq==k&_{0&*%p>8P<002O{=STnm diff --git a/src/main/resources/data/create/structures/gametest/fluids/in_world_pumping_out.nbt b/src/main/resources/data/create/structures/gametest/fluids/in_world_pumping_out.nbt index cfa909345d2c68f3f4ddd38ee3bf207e3030ea50..911d2aaef60360f1a5e0427b280d31c326ad7cc9 100644 GIT binary patch literal 948 zcmV;l155lLiwFP!00000|Gk#qZre5##*Zw?w&VuwMgQ#`))&aytwGwL0fHugvu^8+ z$4JKx5lSRTDsFq#M<~$CUKH4q6nlo<>q7 z9-7Fb33@O)jgQ%Be9TVcV|E%J^PmT_)A*R3#>eb5K4z!!F|W^~hsMY3G(Kjh@i9A% zk2&hW>@+@Rr|~g6jgQ%Be9UnVW~cEnJB^RoX?)C1<6|E7V0IcGv(xyPoyN!PH2>5$ zg4SBWmbwU9%w3w$qlp^bbVg$Rk#W>&OBrBfIk3oM_Pc>tr6iZNO)U<}Ctt7!g`6bQEqT4E;2T<>Bb^xF`I zdj+pbZC6TRe8e-NDK8DiiA4>5HqUp>_=_2znDMn4ADWoa0m%@epPo-o&66<B8+{{F=}H+qSB$>l{1<9#AXM)ljnFE&UtH5afA5y9ZlZ0xp) z_~A(m!>N|2>g%^=+G&_FR_W|KH40wI44XrE+&YABw)K&=3B$DsljBN@N*`)0)D={c zK)iZXmqrTdVcA19JfKXYY_P9ViclFqykF;20(YKKMa~%JQ^8lOH|^k7J$;>cPm^Zd zQ&QHiAcGX*BfLcBO#c48HGX(XZtAWsy*4VJdxZe?fbe>&28(Ajd9$R;7IT# zO4BkUEh`+u(EMtnxo&qOCNs$(+c77r;$VESFp=UpVX8A!SC{V-F(PlMg5b>+*V>82 z@*I)%iV_m0yF+%i_EzY&aW?{O_T%>SwK+Lomg_ki>EAhJ!%ug^tp<9DvpT&@5KEvPlj+6K78zJeYXUlOMtp@uu-(cdBjf>f8T+CME zVzwF=bF&7s)wr0g#>H$kE@rE7F|!)XR^wu}8W*$GxR|Zxx*GH*e0f)#F>iu-n#Lc~ z1mLH8ngQF&5Th?=W^R6+eyU>PX%>;v3!sTyX=-ThKS&Hm?MR;kYISgQxWob zHyb2bK&Ip%52>$v&q#ci#^*FH()fbLJ6Dy(p~v6#OcV0J<9koelJw#zbAfbvnR`sWPzv1Z!yxRjVgrjd_Vbmk@B6 z^L@R*MeYGxk%Lqly7^V1_+CuAp2@;{Ri(4#@<&N2G}6-VHc!i2wh;h(Oy?`M8?bb0 z>Z-NP^FHbarzTR?law8g6gxZ~pqV7JuCcKqQ%zTR8#=vsAfk1iAl$gX98z*}A6gc` z|7lh_92T8p(qWa0Iu0?(^vNZMWz5x<)C~GxF#LCi<%yZC4*VWhBBd(dphmJE%VCw5 zxx(DoW1>W%m*ND15JV1;mt&9*NIZQ{Z#tz5SC1cdKffj=} f!re0Av@NS#^BuaSgiPr^`vAWIBT%)Z_6q<2sZpW3 diff --git a/src/main/resources/data/create/structures/gametest/fluids/large_waterwheel.nbt b/src/main/resources/data/create/structures/gametest/fluids/large_waterwheel.nbt new file mode 100644 index 0000000000000000000000000000000000000000..51263d53d6fc8fbf3de74d15ef130c49c46003b6 GIT binary patch literal 804 zcmb2|=3oGW|7)k8&b#a&(x(1V)h{T+wKd^j8*kFdS-ehbwN#F{uDY_;wA4>%wQ#?w zv0`p~gY0?+>3I#jkLFge9}DZtp84_Q1)-DOSML9LKHFH_RQT92ohv_0S$a0UlDw_H z(cdJEQ$)?&GUM1Q@sB$v+-#k9LqGqxj>ghsj%U>qCYs$jP}b5dSz+MJul6CqsY$ZJ zLfP38j`(foOk9XSv%h{w&`3{#qURS5OG=)CBg;<(Zf!+y)Q76oUGCh800io6H#V9A zL%ZKScU*q$K;eHLt8=wJ4hD^juU#l)+r2gB`IgzjUkrAiJGXAmjpTP57l%!kto$PR z+vBTxk-pXqsi&#kS?rg3!@3vYa}g=NOZDu3C14}Wbnw240S#$(;7t&E{} zJ=}vlj%F==(bHXO`?`hqvt;!I%?%UvOQ$M@2lW2f{b$4C9nUrFQ!lprGKS9m@ci>~ zdprB;(r<5{y!yU=@B4T)oyv6gRdc#qTHpWVy0JJk&SBY)J2!4+&2^08ii>%%_WI+s z(;s9pzFa0XCvO_Vp?Q~t70s8=45^*HpxWcz-^Fj&N>z6Bns9AVv%Yz6!FOF_t(^?X z3x4gdm;c>d`MfK8Zd%OqqfHM_|DF9zx1}~;-Qwqp6wXz@Oeb!rIJdcrzcKrAPt~U0 zoKoYhyI!AJldE5?)b{#Q9^*r{cdrD_Jz4gHU$FD<(bCKXGj7@noZWfhLW|s;k}YSp zPGr4#sVpGF`@Ws}CGVAIt{rQev05l1#$)TFMVz%aczvfINWc5!f?9K~x%jsUyP< zZ@F%4d6arVjQ;#{Z-1GrU7vLG!LH7idz;>+J>R3jWM=!UZGPjbxi`LO|7Je_K*irm HfPn!32S%GG literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/fluids/small_waterwheel.nbt b/src/main/resources/data/create/structures/gametest/fluids/small_waterwheel.nbt new file mode 100644 index 0000000000000000000000000000000000000000..ea60331a79eb9a6b728fc04a432787666571463f GIT binary patch literal 687 zcmb2|=3oGW|4XNx&%5j((60W?s!&KpOY6u??nfthJH1=FwDkT=)OsqFJJ&P0^zqg* z^BqrFcYGCna7ehoagXr(16Hq=Ox$?fyYq?j?EL!AXW!&K;XEMGa@IbXhvBR7r}G!= zPf7&o#Jnro5>S84z$V;k_ZH#*$4?s`S-NdY#{W*mjeb^pVlN)!VVa@Q(8(-u(4d(i zJ;?%&o@*SKDRB;tD)-AX@;vu}AswbUoevRcGOE_Z24llHFjTu=y4h{5(8qDYlW7iXrK~|9w{q1pnY-6s?_TbCexA_fa{XUfuDa(6z1ONKTUFVY_yLKO zHrZO)Yf=YU9zD9FI$_S#4(}Up&wq^0jJH0GBN`VRxsF1YX8RN8efVeg~&pUx^z-8N;u)2D@6e~RvZJ^$RlzNXS_cD?o9e}7*e z{63p=$@@QElf9=lRp^^@_?uNW&YYL9ny+m#wy~Kde7?tPe(9F0T5enn zL+#HGc{$>z>fa};$@l7Ayz)L)lPBD0b>+q@p%Z_ZZ_;i*dZ~QgnkAcdoVVO{TC@0d z$wkSQOS=v$JUsL5pJiuk_T#+VnvUIif^Ds4(ks6$?4P}`+w@ebapaE_mO#&^{8v|8 z5uH^#zr0j<{@ZK0pM!s#%nu28vF>?wy4u%zQJ%OhYj4PYShvviwo$8c<<#4)A-i@& ytSL&4E!MyNuE0Iv@SdmB{?7M$9ov!k%gxHtJ-N8vA>o(kKZb%u-p}267#IM3-%#`b literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/fluids/smart_observer_pipes.nbt b/src/main/resources/data/create/structures/gametest/fluids/smart_observer_pipes.nbt new file mode 100644 index 0000000000000000000000000000000000000000..7da76b1e4d5a7ea689fc8028811691be732094d3 GIT binary patch literal 1171 zcmb2|=3oGW|7XMF^B=ni$R96zr1F;0DWJ3efU(nxOP*7lN@cFbF8%hez{5qldh(iG zAERD>U20nxWY45m1% zXKpb!*j22oI2Oxr;(yiCTW_5YBrEqk(0sDruH<-7>++Wj`enZ#sA(;2Up1e*E^Efa zr^3Dx%UPFQW_rBsK!vWsKGw%u9=J+K>mRyNA$aUqw}Dw)%f^qdu!yaCb&DzaF{;uy zZjfrFo?h|9oIM;6+5N1jW&vfxx1)=lDgFEO${P#b8GgK%Z#G0ee|L!CPDM`JgZuGE zVxFtjz5Dy~(e>!v`)j{H=|667_y5&T2Agw1%^volGiN$7$O;~mUid5{rCiBauvci} zhqjq7u6%ki^`BPs5C1hKsU^!ztfuPtx?i6a=f6LG=DWHjEl;F&zHN20d0KuUWnuS& zi#|)-#M)Q$-+ip(`Q&GifPszE^p>&(zZE)*4(olf>s+p+$DeYiLgkT%?wqevYB&7- zZeG9hz3jgaUw`Y@Reis`oB4*-ktwxet6giRw7<1+yCJ@#uJ3hj%=`zx_@ut~eKcL5 zaq5A=?CyxAhvtZ1EPG#IIQQv7YhO)s9Xr_rA6TZ>ib~hyb1bNzkbgJ1m~Z_;L%A$7 z!`(IWW)yGPZXO<)v%oQV%7xOuC8t|{J&;$QG(bx$}MIO*(U-hbDhieLHjzv(;Mh26)`I=u{*Z>pWP zL4doUJhP?i^2Y_AHQ9C-Gx`--U+=wWqstQ5vL&`n$U`cQqO_Mdah z#Ny9~?!QzV%(w2X-S)b*`sKb3rZNbF;8NFMJ5-Vh~RG&Jl zZN78B^(?jwY%tJTS5_8gJu#tJ)P3<&@yxsZ?R6Hj zrg}Y$c&VCo(Dc{Go^wC6xGt>?Ki~7j!hYS2n6mK9q#ZuyCv4#+bidFx^9R d-VS-=!*WG)xn#5q{>c4f+~zU&_&*^A1^~mKQN#cM literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/fluids/threshold_switch.nbt b/src/main/resources/data/create/structures/gametest/fluids/threshold_switch.nbt new file mode 100644 index 0000000000000000000000000000000000000000..ba193c37129be03128fd03186c678ba75dab2b33 GIT binary patch literal 1664 zcmZ9?c`zG@0tWCDU0QVpThgqfj;^YbqDzR7wpwRhb;Ma`Bs{8)vW~VS7MrqFN2JQe zRV;CpI7%ILrA;bGO{5JWi?FUrX^P!>^Jb^>zJGo*-}kBJNC5tuqiHYNBZaF9jb$rk z{@r|$Y)x&yD_L8MGPeuF=4QR*^7^s9aEg6IH#*b|$(VlLI2(AwUcgh#($psOkBPn} z*#LnKd*Zn|hkzqM(83!JhOdQ%1>4wyF9JD%N?NV=*bsh5-mMD-NlfP}cXr%RR@Xdo zCxunvBZh#D(Kpf;Yw)_rf?1Z8s2?HDP2g3oJ^T#PB69!uFQLJKxgy0b$LuM9*LvyA zMyE-Di<$4XTuy_(L06GeWnkualEnRn7$3=iyFCimM(hIrg(~uY#Xs|eZvQur|NV83 zC;pg=FB(|>7iz-+xnO%1F6Ab8aYfYqEyYdN523}hvP)r}H5mcNsC3W4Z{e#q@EXnR<3eyuFC&ZCE? z#8Ovn8tr#OioP8S*KTn24MPkBDvN^ghpe^qU1RGl{hv{OJC{rHU(v4{>iw$R2=RPc z0c+kmcWp~T1u5y?6{(&4U z-xL$Br^X+rpY{2!A$C=m#HW3R^|tQs2~bCN;V*NFqSatK@9D^*D9|&}HL1kZW3)?~ zJtt_ufVsK!#vdRk9~HQfCnuXtXN})mh)2qOL6I{A)9Jcty*Lp#j#9pV*j##D`F<2I!icwfY?pra$txNj|2MS$fY;CjoM4seQ4I zmhu>0%L(YxXP=3_eYNo2D#^w&-8lAo^@vT-HX{%DKyrI$F&mx?+|iet&xr#wtU5nu z%AYC8T9mGaYT}TP;XkrKYZvR^fB50{Ut+S&RSwz)y-Ilw-#bpGBkj9f3V+n_MJ4&_ z{xO%)A0WK>mc^Q>5p5|LQ9I<0I*w6GLV-hE zu9}6e>fyY(QzZ?6kGl-mP&Z-#7on?_3f9`TPYVs}%2XoY5k!MQ4?(bCi3e*~HiY%w z&YrKA8-z7m1v6i_t;>8#^@}8q)&JVrs-00g{A!fCuar$e`%H_SH!%n!|7Pk(tNnN; z4QO0k3U!vUe5zvX-=pHLAI4m6-5{b9nr8a$Es=!&7Ki+?{b!A-PuZqUhs?M8-j&ql zV~sClUS@hLL!G#$y28Cj?9XXweD<@x){Ue1CL!mBVaSmU&Ri>eM%~HDsDp%to}h|K z_0Hk*-mOnE3p1&Xu>hND(rU|sAf@v;=3(wYdlnN6g z5)Gmenp5}D@Kqwdd+d}aA?9W2Wze>FN%j}UDtPPu%J2#}<~k&4WKk=yaPd-WJu$hh zZlhvyeCnO8>LA7OmQEU{2qkRYpITwH0z+Kfutmw@`?Ky6MgYh zH%q!iyukJq($hUb+|)#s-5G@NLCRQaF2PBP-V)OTPfjK z^m()?2yHSvdKEmlXY8X*h@qeQla}&EYoWZ;5$oFP&^oOcG9T@z$hkvon$;=-9+6PK z0|$4@a!>9OE>+yEj8fEg@7Zo^9(SPLLnzuoAW%fmG6wC*;b(~J?{GA~0uCfsifPDH HRRG{`!GK37 literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/fluids/waterwheel_materials.nbt b/src/main/resources/data/create/structures/gametest/fluids/waterwheel_materials.nbt new file mode 100644 index 0000000000000000000000000000000000000000..0cbebcc7ff2dd4b4733ceb5d465785e44c4e1234 GIT binary patch literal 1093 zcmb2|=3oGW|8J-J=RbB4IWRxjx^PlicY~HUTAfK0BHQ}GtXLm}!`j;tH3F7A1tv@b;VSK$pdd*M@TYwhh?yo`HYT|^&W zO^`B`*v}?xA+f$~b85`FX@!>2$DZa$PA^`J%Kuztmt~-retO3o-N!}SdQMxecnDO5 z&?0lK04RFX2&NBcGMksaK%;VC1(wBMTTJE$G-*et+-YKfey&umAP>x&6M6`~I(G zz8Ks!h4Zl>%krGA59drg4X5QSkzeg|E=eY3&eG{zMT^b37w_bhKBL|-=T*g_qkH7J zcdou#wN&+9)!9u}UNc_56|XX!|FYteqSKiapYuug#7{r{Vb5_?L|GCh!Qmc*Q-{*hF?cU5)nX{_SLUeP8@IOJ@ zK)c;emWy7k&vLl@Q}a`z|Klwh$J2}=m25fseO9FJx>jiv+w19g)@Hr6iqg}op(f6Sl^eW(ItDq6sKu3sZ8yAryLJ(a_#{o!j+tJUHbx##4@%KE z`?`?jtl-xAU)Bz8%{NbckK6l`XUa0w)#+PU%9muE+4NhL<5@*~dxFNZm#>#{Xs|kY zto06foSEu+o}=UVS%!r|#aP2frIoj-K zxk*QU!)uqu^1bH@Kfn9yrCL<8@!09_@z?*jerV&AH_*HEF=2|wu6G4Tb{z7(=q0;r z&idZ#Qo*TZ>2f>6s&<~+xH)p#4>JHE3d!m2giY@|b>jVMGH zS;r-WnY$uP*kNqQY?@2WI(a|6pZC1aKi}u`eV*s{d9;Y~Qva#%pD|&Pf1Uto+~~C& zTd%{2&g8DieOYzKn4CG4K>=7hf89tkxGHtHBZx$>uY2iy*1EF<5X%F?JX8|Kvza9V^DxPps|fE+BDVN)^Rs=W*e#+o)_cr zpc9=ZFU8?|K>zK2h)(yGZTE8&W3&Oila<}d-Nre`zXuxqUts&+GaggYex7WD;<@tk zhaV`a`rqPQ*;aS`A>;{3pU}d2t1_Y01p5l_vhCY;`1ZP6RZWxE%)VmFB6PoeficA!Oc(n8U_E)7JI5;<_0geYIUg_H5b&j$ zaO?2}3u(6tSf? zUYvvorb8Enx20sqBZl6x`yj`#)*k@3&CtQA2(+-*6rIc?{({U$nB=HI^Fd1TT(s$V zJUqsJD5$)PL3Rw;)HkMK9GZnu?x)Bq+}k$L!ZBbCHV z$wjr2leKn+ISuJaR%B4daH;3?gzZ|U-n7DNm7}!_P{sn|(aG@4MycCX?iFG0V-MYf zZaQ>kh=>NG4~;;C3LfH1mAn@Op;i1bCZ#f8GP)|Shl>|m4*{@zLz9n49 zULreYyGt%vL26m~d*rhlE|vsKcNLOreU=QUI|t#CM6EU2h`w8m?z!XX4F@54$~_-I zSE~J4Vk-++ex%B}&S8DM&|7o6o%c2;X_d)OmP?GWw zE!Ti#Knk_^sIIG=jL)us*)Wk2aRFu#MyI@Y>tTV_ftpH=mNd=HCbV6VL`61xPJK=rW924M zg4wRxUTFR}<)^Athf&xEFPV? zzbAHm`}N(=*3eU>{$t8E%^D;8%8^ zxn(Mc*%;L?R25wJ-Pd;>*vQS@$5ogP)f)M9XovQ53Y2T&*vboM)jSt`T9K_ehAOr#i%b-B5tX+O!yx) zdz$@83VN@N#WB@T(%vH~;RVSO5%$+ICypu9Uqyd*G5W@0Dmy#Umm|SvAD2`c@>!)AQc`~bYR0$z literal 2151 zcmYk4c{~&TAIIfOBxi1uEjM5H8H)BD`C^*zHABNlUy`dvIa`jDB*$DO=9pt7lw{1r zTF&XxcVlxTXKZrhTK)9<_V_+}|Mz*nUa#lp^~W2SA;|Op`M&Huc6aQR#3je_6=N1U zI#*;G=*0sE!J80X)ubFd638Zpn{xcUs*~*P zdi?ThPjUzkmd@DB0H2+H+WYHuH30C7JwzYJNnh}z0~d&Fq8uJy2Oh^R%`PnljUxUp){@2 zaa)5+p1hD%urOs`FZDX~V+Bf^-q*9v-S}zQf4k}bk`MkhZ{o!HH~a(crS>!YBe{I! z$7esuXMQ2Wp7H%`vM*{NHF~JpWKT5%?2$rkOjFeM6U6(jvt}*381dn*q<0MS;nenm zsfM9qffJJu_{oQRHI0M~|6JUs$8;nuG9}p;_`U4p^QdWq&5a2bmu)lgPdy8cOD}iN zK9eNpNZZYiGhSU`5|hjj6triIoDk?stTtBo@Vs6D-s`#{li^%dd%{0B-AL7eUG0#? zFBU!n0f3@pME%!-xDuz_w)Q0B6MrjBN^|9!hlk(Oh3ej{r?hLl#;jxYIs~T}JGC5Y zNV(H`b`klF21!NfimQW1i3tHPRjW<_a$apv>!TAR6W;|b^Jkh25TW@tO`%8kn$eyz z6MAHqY1-E$i|NMJS{2?}Mtz*2TQ{_`DD$)>upZvRZ+8}@P_* z(o)L;A;N0jTtr~cU#+&S41D13uqr_2MvjPKUTEfpY!Nv92)%CKYx19W?WeaNAD97c z_CKac7l-tX#>&tmu)5hpjJ(kP{{YKB0M}sjdXc`D56`rY-b&Jl-xB#2S;ZqWM$F45 zj9W!YSdLu3C`5w_1q|g&hspS9rdmB0nhU7Uj0S6HRKy-g>#y-vt_TY|y)Cym<+^_z zTsK1w_|AeqoI(~b#NRxpZtbC0Y8xqaHU|EdH}Yidq|@%H;drbU%jrSMJ^R<7#KxDd zHCPO(u$?5A7GH^619AAKwdKe4W!vHekBWu`^?2jNkwfGzFLJ8Qg9zV47a~+X>8j$k z+9=3Pf1T9JdGQvhTA9Yp+VzXfYx7KBofmvKcT|nf0+o^uZ#eJ4-QZn0Y$|~x^n!TZ zqH@l6H;HcNW#z`Aq^u-A4cJMU=Y8{2?x=NHl8`yESR}ZcAqeR^nPO`6n3mt13u+0x zSS+le?8?t<8o$-RduM@OUMu;#MTw__Ra3t;QRq1k)2I07g2fl4Q$^?cKO1FsM)Vb3 zbTJ`(hsig3Kja?Ch*C9VRlH5&2yN-ivGmA=N)1I(jk7ahLzme~?VPM{E)Q;>9VAhZ zs)r8E-M$%sJ5u_TE~_BlZf?x%D8MN$5m{Y{>ESh8`}9fG#1eRp%*D#?>4obQeba8N zhDzg{xfW+9z#6J#?iC7L$##h>?0B|vwVYU!ost#s*GE9IWU`HqQER$uT#hETT(iq@ z>;|DSA7yac|9C-MQX%#LqUDn|J&bMno3yKAfH~i@Ams-YOLb05E)+9+fX)&W%&G|; zxnF~hVXYgop0xV!UU|uv!(Ph2a&=zV{na^sW^6x0*uY6wT+IZbrDP`YT_V*kZ z_z^rBy=<#+v$KW8zAGAIb#HosdaDu-G{oof;TP-&ThDYcoWG|IMq@><(x9rl3em&|<#F-c&5oD|lj9_7OJ6JnCb zEeIp`Hds3s0h&GJ+dZC84mWt$Zsofz=Zmm;)^u+8jYZpWV#mC1!iXN<1e;+acDWl< zJ;-l}PmWi=3>HQY*5~$A3kF!GU6_8W+g=bW|~M3 z$rG+?1QHbS61Y+#r}|yO#3|jTO`=6zLmFJ4apZ6=?Jc5@CwsV(Zi_SDkcV zcd2ETqY-*UeTXT>U{T_a52muP88kSmQcu!y3>s}RC61RC;<#UOw9EWr%8DWTDkqG) zNCGP_d@#zW1Or0ELm(wzX}=8s@AfjENGnBAx>Bt!?-{hOiESoruf^yh?09(o2XwV3 AI{*Lx diff --git a/src/main/resources/data/create/structures/gametest/items/content_observer_counting.nbt b/src/main/resources/data/create/structures/gametest/items/content_observer_counting.nbt deleted file mode 100644 index 61719d37359cf244bc72255ae2c6f715fd681dd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 933 zcmV;W16uqaiwFP!00000|J|5RZ__Xoz+YmgP1B8m#+b&jN5o;1IPHSa5YoiN5StKi zsod1LjMW5W7W9QJyg3Ix`> zG@A83eZu-Mp_||wjw8wvovn+@uUAi=e*bd$@Y|2@=VJt38si{{CUtee4!W@JGb#t$ z?FM`plJj;LM5i9qj^YvXsSC~l>CkWj?|0!*2K#X&b5$Nsy-tpv(vq1vQa@efULQp>*u}Bt8 zqO}U4d~Z<&qwB3*+S@;vm=dz)5;LAiSMvIsCz47pKap^jUIU!{zO#(=T{G+S=Gtsp`zMr(iq7R%oKwN*R^0RyQL^IlGXVd`wVduwxf&EV z0#ve6%If6)n{~?Q_2sNLmayJj#(L`(x|?4q^$JF}Qn10f1DDLCDn@r!!X?gCjLsRd zU0~7q&R*cGm__G1dx5iJ7M<@FIV(ozY?V5vD;B}p-iV8Ma7@p{zMR}=$$&0+vu|Pb zb4$q2)^6*`cI(20)i-3Uxt+|!d)YLNg0q0O#i&CooboktE0{&+r0X@hNp3s(+;+@! zvrBqX%*`(CNjamNN47zYP-%_I8QnZW2F}R985uZZ17~dDj18Qzfitdft`A8_g`mkB zui*lIMFzBd_g6Oyr*sdz*DRJBfAX&)SH_FSl+i9YJrZ(x!mag)pFHkFF%u_H93p=% zu8f5R8|6Y_O60piMXLI1z}!rpu9q}xjwQjuxZkhLRE87hLkc*gy_xK_H{8tw*OIP; z^fp2}Ea;#7rgXC#k2)c3%K>D-UKqzL*TFH(b+BYZLeP9p1bIst58?=5WgC70DfN8- HtrP$NQ47_{ diff --git a/src/main/resources/data/create/structures/gametest/items/smart_observer_belt_and_funnel.nbt b/src/main/resources/data/create/structures/gametest/items/smart_observer_belt_and_funnel.nbt new file mode 100644 index 0000000000000000000000000000000000000000..48a5e98d7f56cc110de54e3214fbe8707855ab25 GIT binary patch literal 1661 zcmZ{edpOez9L7=3CPqZAxeGfS)UsTY6_SpXX`^B;L$*djp|;$nMWm7tHYH@ORxU*? zRFZ8jwcKU3qTGr##*aGHd3t)zdCvL%_rBlf{k*SUCRpmnedQJGj2U!WO#vALn0jRz^*sO!gW(A}*xu7CL7h4_0mUhcnG-@rLO--;~C z(7S)ZeiAD7etT7Z`PuQ){#IC;_D=Yss?e7@x`tnnMWf3aB8?`FzK!A_s`*6H0 zg3lN~<7+c$kw6L;vgBbe)wDoD|6H_5e*T?p=Xuqw{tPYMgBf-pgn{GQUdEdw!V6y+ z*gW9KAFt6~Cn^cpbC-Y>UvBOU;SCULESs|xWat9I6cr3C_N{yoEY9v)Qx#y(VDU1B z#NrRc$!6!1H+5UtJZR(U!V~dxqrxP`>U9w&Wp6VgNv?mD7)xxLpzom zo51XjxOcma`F&@v>t&Wp>Z9{q^T_G!OdsctB4RM+ha_RD(V%z5X zYf~TRKk1pry~UF)<}QG!w|w78&X~F1py^D=er}hF>07)wCa1K8^m$-NO2!{(+Jr0- zVU6`WwycT8n>Qq<4dtg54O@+H(g6T}Ww;Mwbi<7~htuf&P3LlR6@o0KZed$rkUN>B z3cu-wWAUsKYRWeq%`won)5sP`qq77v6Ba3+g-lU@e-Y?jY*80^BYB1#*22A`{#cCgkMr11D`BPtqFm z@a`L337g5QJ$UWucor(E*eB3$#z{ZVms4mNmpPtZU-@htp$oo|E*-5s8kC{*IM#Rb z9>>!S#96a+cPnlU9XVRR12U?>gN5`Qpj6m~DMv6DUB`0xiPnd?2)|DrkFwNEkzN|x zxgTcqRkl+oq^qz05SVhXUb1$T3t+-2my?Yyo)PVf*6Z4A6%N^Cc2eJh<27pv475)j zUiw@Jk9-~!Z-%&)TT*K@vhky9RoKT#Y8zNxf;*Q=^Lkb0Fq78?6j6$?sX_!3RxQI( z5cZ+jv?GmyZ2N=7-H}N$rYL=2c>~jHG#S=85>mBA&c!v3COG?yFWg6FD z6WHue&WC)GT2lRPal&}A$J9)Z^tW*z>|G>BCcB1zwYzcgr|_6QfOBeQl=nwj~ZT zWaeUZpZpVl$i!cAC=65<5FaGrf%e>U&pC51ECA-fOnQp}0JuA{cQ}U_C8d<2f%2L| zv=J)#Y!>$6=^TPw8CTF;0fdG)VO`OO@g`vW37B94hK*pn37Q>dOtZs`X?B<~%?>lB z*`J`4`HHT*6};* zEKK=L@GRvAp$Mjq(c}dS?=09HW;qu~gk|SA2KJ6;2#;#Z8_kVbC)}iRZK3;U?zk`rj_E!p3(=IR#Atp@>v4Vh0 z2=vesbOS=h9!h{|)J{<6bL=qPx${4ExV^?6D3XE%D ztP0MWF*$3-CN~ zle1<_&YCefYi8Qn2PY1t4@(`y`&b_IdIC!athc$j^)9!w-e+fMfp;*BG0$Q)&d$GF zY(4(|`TYL3AN1!#3s#>au_Gmlj&OIE4U$wU(g8n@-Y4MD_VAj+NyYi9MV z@vLjl2vw4~((*tVMjZ9zHX_^tZ#RpzRLoM2?F_ODD&R>zV11631NBbIW3`rwty$o{ zPCCq*BQ{ww`(F^VwOpy#AN%A&9XGH~?2}a^2!)pc15uro1Ow+WhX@4>6 zr3I{SEn!qZflNbOvNhQ=PW3A}ZJM1nt2^2HNuP3RLOnqgONmF_-2nFm8sO5BD}VnbXR55( zzmbL-$cC8XE_gjeh1yDLJrre}7c(x8U^Z)V1Dc!VN}f(cxfltlzRJiwFP!00000|J|11iqk+4$0yCEZPF9rRPG6UiTfa)9tVPG!PBp0n@-x` zCL4Aq7JV6C=)hNUx9O%OCVRn8VjzUJ|M|~vc4jwC0AgS||A8R@*gG;ev(FgjSW-?o zYOq%f)3uQ4SDly-FJlNRsa;Teg%D}tgvW?JO)x?;8KDVBXqZP6j9_*eAG6c=n4QMQ z>@+^+$p~hr@i9A%kJ)K_%ueHDZu>Dp<70LjAG6c=n4QMQ%tkOfjgQ%Be9TVcV|JRy z<~iv21U>x1rWWJbxDDVx@golh5f?#yl&#IQiaC&I+Z1vQ*9;J8r3&c zAGTE}Q2c0ZEg~mDKgjHGmZSf;-q2I8n`Zlp4~*p}A?Y~Pmz`OZ3Qq-%-sa)J;Cm83vI$1-xj$3K zFsrygBD86zb6_8Mf$;3O&}+rDUZq^;<8*_W#fvLXi_h85bJC#a*2k6mB%h z5?6v3RX3Jp+_XB%gp_7ZW|iEd(E}q@;~b{uSDP@>vZh^YepaABvz5E1i@iZsLT
    Gv7+x4!qgTdu+O?#yN-2BIAl8|uC_?P_S|fPb~y$Pp+Wn@0<_^h<8E5E YxaGvZpwecx0Tw^uH|MQG5y1)o0IVHBkN^Mx literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/items/smart_observer_storage.nbt b/src/main/resources/data/create/structures/gametest/items/smart_observer_storage.nbt new file mode 100644 index 0000000000000000000000000000000000000000..bd344d8dabf22ff7f64d7b57c7673f388216a1ce GIT binary patch literal 541 zcmV+&0^=UW2L}Wjg-E{%pfV;eb7X@dKBha>f@7eja7-= z5{-m>a|&rIZfIQ|8p5`-JzR!M;nowcv~`z+RwG9r4-O(79IL-gB4_zkYC0Y7yFSZa z%k8?ocKS%OcOdy^2MV~UxkMuLZL=KMXI>$^J>}gPZtc3{R;azhT&#?)(fAuMheQg3 zbjPJd2zB>8gTbvOt#B=QRS#>~j=PDoBbPFF1p0_h4vb8LIb`ln-!HYgp~KnxBA-OJ zU_;O4(GeS|_j_m6ab0*RZRp^v=vR%9`8*P@S$CrwSf2^UY%}6!kJKgVr!n$sjM}#Y fIG0~pI(K5rCrRe@7AN6-LYoJJ~=c9QS#$YfC^Djg_PL z&F=U8_`LUKJqbVuqEbA>5CH5iUff$9Xk%f75m+X)W(kDF&?FG|G#5nz?HqHZ>8;hT z?@qPp%|8-3mWJmOGJpg++e&BH=Y})5zNL{H+($D;Ee!U>OcYs9m~x##vWt8+?5AF$ z-mg7$ptDP9?HSCgp$zP!kg%(`r?+a==H1V$`{{!lP}eE4qy6NG9cl}=w2x}|*GAR+ zZxn8?4AN?|42IPPfq2Vy#%@T%vk+Q+p7RXduvibTo|W&+iNq`;FO3{rzmS665Y&U? z3)Z$=MEj*PeSKB-W0W}>e{=?G`^pagfg~SPsG)DuOuZ4V{wO)9YaS_PF2{iyDf zMcp@}+Si?b7x(|bwX0RxSGB%bTR2C{D}D*9ICNm(%#ToW{rHG(Ik; z@o_~BxSYnvi!9agdN7^pC4{-kg=Yma`8}s=mu+zSo>R- zZ10F1sLHLqP+@pNhx5?%4pp_zgi9Q4t_C&-R0Pk>Buo?$cA?w#dwreL*L_ll!RS+PZDvX*L}35(Bk ziT@mx(|Yq&_Uf=lH|r(#O-S$d6u+j_EEG|`K&Wi~5+xJMC7T+{)o&)|x_EDQUpwp7 zCtH7o-wV#ms_NZdo&M`@X2ru#cWjTxUT2GabUUj2$+;FUd7WAE84_>apI`R-(zfaL zOds<9?v1-uoMX2BZvB(vVt=`Ae0Ne5e;w<}M7;lKP!0&}GH6xR#38i#YyiKus^4FmX}jevuxhWpd+Sa0-OGwes$!*jf>leTz3<^5t9i??8O}@7_Nbfw2w%im?2~ literal 0 HcmV?d00001 diff --git a/src/main/resources/data/create/structures/gametest/misc/threshold_switch_pulley.nbt b/src/main/resources/data/create/structures/gametest/misc/threshold_switch_pulley.nbt new file mode 100644 index 0000000000000000000000000000000000000000..a405da75ce7b043f5b51a74535c66c9d0c5eab9b GIT binary patch literal 839 zcmV-N1GxMjiwFP!00000|BY46Zqq;z9>;d#rmgs?5E2px-T;XkQh_v~A_vl_N%^}( zTYKED*m%wEI%zpo;z2-&SKz>nSK-DxR3gkKc1eRhE5i{*l^4KtW~l-W2HY--ooI2E`G zo9noo3S6}cT(t^ZwF+Er1unM&ms^3$tH9+|;PNVP`4zbQ3S52#uA+gLaU5uZn;e=Z z)OWcsr~;_*2sXz&!BDZ-JZ4-u;J0O#7>CS*ZZbh-Fe1ueFOGLm$mx?ai@vcyXpOBc zVLw}7-3=8I*<)?!e5739NKd&5k9}zDC9Oj_!Kcq`0*!X26wW<~r_q;FR7B&zt^BqX z6M6>)JEfQ2K{}L9N!R)-8P@U)UD)gzeSMlyk0Y6BrhN8 z|5!`fe>G?~54s3&XD;x~ZlaCKLc?X!O(Hy*(IT%QAJ5|^ldUC_PNtaMt(5~?FPLr_ z!@^^X+H#y`Bqp&mdT6Fny{BrnS;|6gP8UetfaabJ&x2Ywn$7;qX6OBFN@QRZ>SF3` zEb+}}Uo8M{c$y+?4iENwqfU1)Z1vjDDat}HFM!~sjCjl`fX43L{_EC$2MRa$d9Sl% z>|9}NCBIiR7JkSu2{9TSAyfL8$E4e`5Pc<&ikKZ(?<<@X-9>#rkeLc;!aHwBr*qoh zlG!%x@96n^*&$l3mRC}(Qf0emmz4EkvlT^Dj{NiPnZ>$N?aQr4!Tnt5u#xL9mPV?Q z8MGR%Pd1H|2{Z<6$?BwikBt$wa-c)Sv>t^_+tzJDvu~SP*?%2b#w~5~y$A3UD4S0U* Date: Fri, 30 Jun 2023 01:37:07 -0400 Subject: [PATCH 3/4] update bucket volume constant --- .../create/infrastructure/gametest/tests/TestFluids.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java index d037f4578..b4c0eef54 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java @@ -14,8 +14,6 @@ import com.simibubi.create.infrastructure.gametest.CreateGameTestHelper; import com.simibubi.create.infrastructure.gametest.GameTestGroup; import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.tags.BlockTags; @@ -254,7 +252,7 @@ public class TestFluids { BlockPos lever = new BlockPos(3, 3, 1); BlockPos output = new BlockPos(3, 4, 4); BlockPos tankOutput = new BlockPos(1, 2, 4); - FluidStack expected = new FluidStack(Fluids.WATER, 2 * FluidAttributes.BUCKET_VOLUME); + FluidStack expected = new FluidStack(Fluids.WATER, 2 * FluidType.BUCKET_VOLUME); helper.pullLever(lever); helper.succeedWhen(() -> { helper.assertFluidPresent(expected, tankOutput); From b047db0ecf69c78e5a93ec862c44f5150afd1caa Mon Sep 17 00:00:00 2001 From: TropheusJ Date: Fri, 30 Jun 2023 01:51:51 -0400 Subject: [PATCH 4/4] fix small waterwheel SU expectation --- .../create/infrastructure/gametest/tests/TestFluids.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java index 354a02a65..a0a4097e0 100644 --- a/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java +++ b/src/main/java/com/simibubi/create/infrastructure/gametest/tests/TestFluids.java @@ -174,7 +174,7 @@ public class TestFluids { List edges = List.of(new BlockPos(3, 3, 1), new BlockPos(3, 3, 3)); BlockPos openLever = new BlockPos(2, 6, 1); BlockPos leftLever = new BlockPos(4, 5, 1); - waterwheel(helper, wheel, 8, 128, leftEnd, rightEnd, edges, openLever, leftLever); + waterwheel(helper, wheel, 8, 256, leftEnd, rightEnd, edges, openLever, leftLever); } private static void waterwheel(CreateGameTestHelper helper,