diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 875b35587..345803a49 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -44,7 +44,8 @@ Valid examples are: ## Translations -If you want to contribute to the translations of this project, please use [Crowdin](https://crowdin.com/project/refined-storage-2). +If you want to contribute to the translations of this project, please +use [Crowdin](https://crowdin.com/project/refined-storage-2). ## Versioning @@ -126,8 +127,25 @@ at all times. They ensure that there are no regressions, act as general documentation for the codebase, and ensure that the project can evolve over time. -To avoid brittle tests, tests need to validate behavior. A test cannot rely on the internal code structure, so most -mocking should be avoided. +To avoid brittle tests, tests need to validate behavior. A test cannot rely on the internal code structure, so most use +of mocking should be avoided. + +### Unit testing + +Tests in the API modules are regular unit tests. Don't see a "unit" here as a code unit, but as a unit of behavior. + +> Don't isolate code by using mocking, even in a unit test. + +These tests don't rely on, nor know about, Minecraft. + +Additionally, tests in the `refinedstorage2-network` module use the `refinedstorage2-network-test` JUnit plugin to easily set up networks for testing. + +### Integration testing + +To test the entire chain from Minecraft to the API modules, we use integration tests. These tests are located in the +test source set of the `refinedstorage2-platform-forge` module. + +We write these integration tests as Minecraft gametests using the NeoForge testing framework. ### Test coverage @@ -202,6 +220,7 @@ The build workflow takes care of the following: - Running a Gradle build, running our tests in the process and generating an aggregated code coverage report for the API modules. +- Running Minecraft gametests. - Analyzing the code on SonarQube. > Because of > [limitations with SonarQube](https://portal.productboard.com/sonarsource/1-sonarcloud/c/50-sonarcloud-analyzes-external-pull-request), @@ -249,18 +268,18 @@ Refined Storage 2 is split up into various modules. Most modules aren't dependent on Minecraft or a mod loader. Only modules that start with `refinedstorage2-platform-*` have dependencies on Minecraft. -| Name | Use in addons | Description | -|-----------------------------------|---------------|---------------------------------------------------------------------------------------| -| `refinedstorage2-core-api` | ✔️ | Contains some utility classes and enums. | -| `refinedstorage2-grid-api` | ✔️ | Contains Grid related functionality. | -| `refinedstorage2-network-api` | ✔️ | Contains storage network related functionality. | -| `refinedstorage2-network` | ❌ | Contains implementations of `refinedstorage2-network-api`. | -| `refinedstorage2-network-test` | ✔️ | JUnit extension which helps with setting up a network and a network node for testing. | -| `refinedstorage2-query-parser` | ✔️ | A query parser, contains a lexer and parser. Only used for Grid query parsing. | -| `refinedstorage2-resource-api` | ✔️ | Contains API for handling resources. | -| `refinedstorage2-storage-api` | ✔️ | Contains storage related functionality. | -| `refinedstorage2-platform-api` | ✔️ | Implements the various Refined Storage API modules for use in Minecraft. | -| `refinedstorage2-platform-test` | ✔️ | This module is used in platform tests for various Minecraft related helpers. | -| `refinedstorage2-platform-fabric` | ❌ | The platform module for Fabric. This module contains Fabric specific code. | -| `refinedstorage2-platform-forge` | ❌ | The platform module for Forge. This module contains Forge specific code. | -| `refinedstorage2-platform-common` | ❌ | Common mod code. Most gameplay code is in here. | +| Name | Use in addons | Description | +|-----------------------------------|---------------|----------------------------------------------------------------------------------------------------| +| `refinedstorage2-core-api` | ✔️ | Contains some utility classes and enums. | +| `refinedstorage2-grid-api` | ✔️ | Contains Grid related functionality. | +| `refinedstorage2-network-api` | ✔️ | Contains storage network related functionality. | +| `refinedstorage2-network` | ❌ | Contains implementations of `refinedstorage2-network-api`. | +| `refinedstorage2-network-test` | ✔️ | JUnit extension which helps with setting up a network and a network node for testing. | +| `refinedstorage2-query-parser` | ✔️ | A query parser, contains a lexer and parser. Only used for Grid query parsing. | +| `refinedstorage2-resource-api` | ✔️ | Contains API for handling resources. | +| `refinedstorage2-storage-api` | ✔️ | Contains storage related functionality. | +| `refinedstorage2-platform-api` | ✔️ | Implements the various Refined Storage API modules for use in Minecraft. | +| `refinedstorage2-platform-test` | ✔️ | This module is used in platform tests for various Minecraft related helpers. | +| `refinedstorage2-platform-fabric` | ❌ | The platform module for Fabric. This module contains Fabric specific code. | +| `refinedstorage2-platform-forge` | ❌ | The platform module for Forge. This module contains Forge specific code and the integration tests. | +| `refinedstorage2-platform-common` | ❌ | Common mod code. Most gameplay code is in here. | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bfd3a654c..94f94e34b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,5 +8,7 @@ on: types: [ opened, synchronize, reopened ] jobs: build: - uses: refinedmods/refinedarchitect/.github/workflows/build.yml@v0.11.5 + uses: refinedmods/refinedarchitect/.github/workflows/build.yml@v0.12.1 + with: + gametests: true secrets: inherit diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index b34ffa667..6fad3ac91 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -17,7 +17,7 @@ on: type: string jobs: draft: - uses: refinedmods/refinedarchitect/.github/workflows/draft-release.yml@v0.11.5 + uses: refinedmods/refinedarchitect/.github/workflows/draft-release.yml@v0.12.1 with: release-type: ${{ inputs.release-type }} version-number-override: ${{ inputs.version-number-override }} diff --git a/.github/workflows/issue-for-unsupported-version.yml b/.github/workflows/issue-for-unsupported-version.yml index 7aa68ecb9..1664acf15 100644 --- a/.github/workflows/issue-for-unsupported-version.yml +++ b/.github/workflows/issue-for-unsupported-version.yml @@ -4,4 +4,4 @@ on: types: [ labeled, unlabeled, reopened ] jobs: unsupported-labeler: - uses: refinedmods/refinedarchitect/.github/workflows/issue-for-unsupported-version.yml@v0.11.5 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/issue-for-unsupported-version.yml@v0.12.1 \ No newline at end of file diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index bd9b29525..cf971feb5 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -7,7 +7,7 @@ on: - closed jobs: publish-release: - uses: refinedmods/refinedarchitect/.github/workflows/publish-release.yml@v0.11.5 + uses: refinedmods/refinedarchitect/.github/workflows/publish-release.yml@v0.12.1 secrets: inherit with: project-name: 'Refined Storage' diff --git a/.github/workflows/resolved-issue-locking.yml b/.github/workflows/resolved-issue-locking.yml index cbfb9219d..60236446c 100644 --- a/.github/workflows/resolved-issue-locking.yml +++ b/.github/workflows/resolved-issue-locking.yml @@ -4,4 +4,4 @@ on: - cron: '0 0 * * *' jobs: lock: - uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.11.5 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/resolved-issue-locking.yml@v0.12.1 \ No newline at end of file diff --git a/.github/workflows/validate-branch-name.yml b/.github/workflows/validate-branch-name.yml index 7f4ce5e06..c862e1c82 100644 --- a/.github/workflows/validate-branch-name.yml +++ b/.github/workflows/validate-branch-name.yml @@ -2,4 +2,4 @@ name: Validate branch name on: [ pull_request ] jobs: validate-branch-name: - uses: refinedmods/refinedarchitect/.github/workflows/validate-branch-name.yml@v0.11.5 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-branch-name.yml@v0.12.1 \ No newline at end of file diff --git a/.github/workflows/validate-changelog.yml b/.github/workflows/validate-changelog.yml index 8b90c5051..8c6b2e8de 100644 --- a/.github/workflows/validate-changelog.yml +++ b/.github/workflows/validate-changelog.yml @@ -4,4 +4,4 @@ on: types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ] jobs: validate-changelog: - uses: refinedmods/refinedarchitect/.github/workflows/validate-changelog.yml@v0.11.5 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-changelog.yml@v0.12.1 \ No newline at end of file diff --git a/.github/workflows/validate-commit-messages.yml b/.github/workflows/validate-commit-messages.yml index d8d12de6b..306fbb8c3 100644 --- a/.github/workflows/validate-commit-messages.yml +++ b/.github/workflows/validate-commit-messages.yml @@ -2,4 +2,4 @@ name: Validate commit messages on: [ pull_request ] jobs: validate-commit-messages: - uses: refinedmods/refinedarchitect/.github/workflows/validate-commit-messages.yml@v0.11.5 \ No newline at end of file + uses: refinedmods/refinedarchitect/.github/workflows/validate-commit-messages.yml@v0.12.1 \ No newline at end of file diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index b0d870283..8b6e49245 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -10,4 +10,5 @@ + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java index 4c870f75e..45cf2e459 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java @@ -57,7 +57,7 @@ public ConstructorBlockEntity(final BlockPos pos, final BlockState state) { } @Override - protected void setFilters(final List filters) { + public void setFilters(final List filters) { this.tasks.clear(); this.tasks.addAll(filters.stream().map(TaskImpl::new).toList()); } diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java index 5ea3d2f31..2b911edb6 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java @@ -95,7 +95,7 @@ protected void setTaskExecutor(final TaskExecutor filters) { + public void setFilters(final List filters) { mainNode.setFilters(filters); } diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/AbstractDirectionalBlock.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/AbstractDirectionalBlock.java index b8f86a1c7..b61083c3c 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/AbstractDirectionalBlock.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/AbstractDirectionalBlock.java @@ -70,6 +70,10 @@ public Direction extractDirection(@Nullable final BlockState state) { return getDirectionType().extractDirection(direction); } + public BlockState rotated(final T direction) { + return defaultBlockState().setValue(getDirectionType().getProperty(), direction); + } + public static boolean doesBlockStateChangeWarrantNetworkNodeUpdate(final BlockState oldBlockState, final BlockState newBlockState) { if (!(newBlockState.getBlock() instanceof AbstractDirectionalBlock newDirectionalBlock)) { diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java index df09b565f..dac1392be 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java @@ -82,5 +82,5 @@ public void writeScreenOpeningData(final ServerPlayer player, final FriendlyByte protected abstract void setTaskExecutor(TaskExecutor taskExecutor); - protected abstract void setFilters(List filters); + public abstract void setFilters(List filters); } diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractUpgradeableNetworkNodeContainerBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractUpgradeableNetworkNodeContainerBlockEntity.java index 6939537ae..27f36f9ed 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractUpgradeableNetworkNodeContainerBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractUpgradeableNetworkNodeContainerBlockEntity.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; -import com.google.common.util.concurrent.RateLimiter; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; @@ -33,7 +32,8 @@ public abstract class AbstractUpgradeableNetworkNodeContainerBlockEntity type, @@ -52,7 +52,7 @@ protected AbstractUpgradeableNetworkNodeContainerBlockEntity( @Override public final void doWork() { - if (rateLimiter.tryAcquire()) { + if (workTicks++ % workTickRate == 0) { super.doWork(); postDoWork(); } @@ -105,16 +105,12 @@ public void load(final CompoundTag tag) { private void configureAccordingToUpgrades() { LOGGER.debug("Reconfiguring {} for upgrades", getBlockPos()); final int amountOfSpeedUpgrades = upgradeContainer.getAmount(Items.INSTANCE.getSpeedUpgrade()); - this.rateLimiter = createRateLimiter(amountOfSpeedUpgrades); + this.workTickRate = (amountOfSpeedUpgrades + 1) * 20; this.setEnergyUsage(upgradeContainer.getEnergyUsage()); } protected abstract void setEnergyUsage(long upgradeEnergyUsage); - private static RateLimiter createRateLimiter(final int amountOfSpeedUpgrades) { - return RateLimiter.create((double) amountOfSpeedUpgrades + 1); - } - @Override public NonNullList getDrops() { final NonNullList drops = NonNullList.create(); diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java new file mode 100644 index 000000000..88523fe7d --- /dev/null +++ b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java @@ -0,0 +1,51 @@ +package com.refinedmods.refinedstorage2.platform.forge; + +import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.platform.common.constructordestructor.ConstructorBlockEntity; +import com.refinedmods.refinedstorage2.platform.common.storage.ItemStorageType; +import com.refinedmods.refinedstorage2.platform.common.util.IdentifierUtil; + +import java.util.List; + +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.level.block.Blocks; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; + +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.DIRT; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.RSBLOCKS; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.itemIsInserted; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.requireBlockEntity; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.storageMustContainExactly; +import static net.minecraft.core.BlockPos.ZERO; + +@GameTestHolder(IdentifierUtil.MOD_ID) +@PrefixGameTestTemplate(false) +public final class ConstructorTest { + private ConstructorTest() { + } + + @GameTest(template = "empty_15x15") + public static void shouldPlaceBlock(final GameTestHelper helper) { + // Arrange + helper.setBlock(ZERO.above(), RSBLOCKS.getCreativeController().getDefault()); + helper.setBlock(ZERO.above().above(), RSBLOCKS.getConstructor().getDefault().rotated(Direction.EAST)); + helper.setBlock( + ZERO.above().above().above(), + RSBLOCKS.getItemStorageBlock(ItemStorageType.Variant.ONE_K) + ); + + final var seq = helper.startSequence(); + seq.thenWaitUntil(itemIsInserted(helper, ZERO.above().above(), DIRT, 10)); + + // Act + requireBlockEntity(helper, ZERO.above().above(), ConstructorBlockEntity.class).setFilters(List.of(DIRT)); + + // Assert + seq.thenWaitUntil(() -> helper.assertBlockPresent(Blocks.DIRT, ZERO.above().above().east())) + .thenWaitUntil(storageMustContainExactly(helper, ZERO.above().above(), new ResourceAmount(DIRT, 9))) + .thenSucceed(); + } +} diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java new file mode 100644 index 000000000..26ffd3042 --- /dev/null +++ b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java @@ -0,0 +1,104 @@ +package com.refinedmods.refinedstorage2.platform.forge; + +import com.refinedmods.refinedstorage2.api.core.Action; +import com.refinedmods.refinedstorage2.api.network.Network; +import com.refinedmods.refinedstorage2.api.network.node.NetworkNode; +import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.api.storage.EmptyActor; +import com.refinedmods.refinedstorage2.platform.api.support.network.AbstractNetworkNodeContainerBlockEntity; +import com.refinedmods.refinedstorage2.platform.common.content.Blocks; +import com.refinedmods.refinedstorage2.platform.common.support.resource.ItemResource; + +import java.util.Arrays; +import javax.annotation.Nullable; + +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTestAssertException; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BlockEntity; + +public final class GameTestUtil { + public static final ItemResource DIRT = ItemResource.ofItemStack(new ItemStack(Items.DIRT)); + public static final Blocks RSBLOCKS = Blocks.INSTANCE; + + private GameTestUtil() { + } + + @Nullable + public static Network getNetwork(final net.minecraft.gametest.framework.GameTestHelper helper, final BlockPos pos) { + try { + final var be = requireBlockEntity(helper, pos, AbstractNetworkNodeContainerBlockEntity.class); + final var field = AbstractNetworkNodeContainerBlockEntity.class.getDeclaredField("mainNode"); + field.setAccessible(true); + final NetworkNode mainNode = (NetworkNode) field.get(be); + return mainNode.getNetwork(); + } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) { + throw new RuntimeException(e); + } + } + + public static Runnable itemIsInserted(final net.minecraft.gametest.framework.GameTestHelper helper, + final BlockPos networkPos, + final ItemResource resource, + final long amount) { + return () -> { + final Network network = getNetwork(helper, networkPos); + helper.assertTrue(network != null && network.getComponent(StorageNetworkComponent.class) + .insert(resource, amount, Action.EXECUTE, EmptyActor.INSTANCE) == amount, + "Item couldn't be inserted" + ); + }; + } + + @SuppressWarnings("unchecked") + public static T requireBlockEntity( + final GameTestHelper helper, + final BlockPos pos, + final Class clazz + ) { + final BlockEntity blockEntity = helper.getBlockEntity(pos); + if (blockEntity == null) { + throw new GameTestAssertException("Block entity not found at " + pos); + } + if (!clazz.isInstance(blockEntity)) { + throw new GameTestAssertException( + "Expected block entity of type " + clazz + " but was " + blockEntity.getClass() + ); + } + return (T) blockEntity; + } + + public static Runnable storageMustContainExactly(final net.minecraft.gametest.framework.GameTestHelper helper, + final BlockPos networkPos, + final ResourceAmount... expected) { + return () -> { + final Network network = getNetwork(helper, networkPos); + helper.assertTrue(network != null, "Network is not found"); + if (network == null) { + return; + } + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (final ResourceAmount expectedItem : expected) { + final boolean contains = storage.getAll() + .stream() + .anyMatch(inStorage -> inStorage.getResource().equals(expectedItem.getResource()) + && inStorage.getAmount() == expectedItem.getAmount()); + if (!contains) { + throw new GameTestAssertException("Missing from storage: " + expectedItem); + } + } + for (final ResourceAmount inStorage : storage.getAll()) { + final boolean wasExpected = Arrays.stream(expected).anyMatch( + expectedItem -> expectedItem.getResource().equals(inStorage.getResource()) + && expectedItem.getAmount() == inStorage.getAmount() + ); + if (!wasExpected) { + throw new GameTestAssertException("Unexpected in storage: " + inStorage); + } + } + }; + } +} diff --git a/refinedstorage2-platform-forge/src/test/resources/data/refinedstorage2/structures/empty_15x15.nbt b/refinedstorage2-platform-forge/src/test/resources/data/refinedstorage2/structures/empty_15x15.nbt new file mode 100644 index 000000000..bec099168 Binary files /dev/null and b/refinedstorage2-platform-forge/src/test/resources/data/refinedstorage2/structures/empty_15x15.nbt differ diff --git a/settings.gradle b/settings.gradle index 37d224592..90c2297da 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -gradle.ext.refinedArchitectVersion = "0.11.5" +gradle.ext.refinedArchitectVersion = "0.12.1" dependencyResolutionManagement { repositories {