diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a713c7a..caa3223 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,18 @@ -name: Sky's Grass Slabs 26.2 NeoForge CI +name: Sky's Grass Slabs 26.3 NeoForge CI on: push: branches: - - master-26.2-neo + - master-26.3-neo pull_request: branches: - - master-26.2-neo + - master-26.3-neo permissions: contents: read concurrency: - group: skysgrassslabs-26.2-neo-${{ github.workflow }}-${{ github.ref }} + group: skysgrassslabs-26.3-neo-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -107,13 +107,13 @@ jobs: - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: SkysGrassSlabs-26.2-${{ github.sha }} + name: SkysGrassSlabs-26.3-${{ github.sha }} if-no-files-found: error retention-days: 30 path: | - build/libs/SkysGrassSlabs-1.1.2.2602002.jar - build/libs/SkysGrassSlabs-1.1.2.2602002-sources.jar - build/libs/SkysGrassSlabs-1.1.2.2602002-javadoc.jar + build/libs/SkysGrassSlabs-1.1.2.2603002.jar + build/libs/SkysGrassSlabs-1.1.2.2603002-sources.jar + build/libs/SkysGrassSlabs-1.1.2.2603002-javadoc.jar build/release/SHA256SUMS CHANGELOG.md @@ -121,7 +121,7 @@ jobs: if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: SkysGrassSlabs-26.2-diagnostics-${{ github.sha }} + name: SkysGrassSlabs-26.3-diagnostics-${{ github.sha }} if-no-files-found: ignore retention-days: 14 path: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 525cad8..2fdc686 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -3,10 +3,10 @@ name: CodeQL on: push: branches: - - master-26.2-neo + - master-26.3-neo pull_request: branches: - - master-26.2-neo + - master-26.3-neo schedule: - cron: '53 7 * * 4' diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml index 8615564..82e114c 100644 --- a/.github/workflows/validate-gradle-build.yml +++ b/.github/workflows/validate-gradle-build.yml @@ -3,10 +3,10 @@ name: Validate Gradle Wrapper on: push: branches: - - master-26.2-neo + - master-26.3-neo pull_request: branches: - - master-26.2-neo + - master-26.3-neo permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 877fc74..156d3cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.1.2.2603002 - Minecraft 26.3 (NeoForge) + +- Ported the accepted NeoForge release to Minecraft 26.3. +- Preserved blocks, recipes, configuration, world data and legacy upgrades. + ## 1.1.2.2602002 - Minecraft 26.2 (NeoForge) - Corrected snowy grass slabs so their top surface uses snow instead of green diff --git a/README.md b/README.md index 51ec8ce..a7bc2b3 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ thin layer of turf. Grass spreads naturally between full blocks and slabs, while new Overworld terrain uses grass slabs to soften suitable one block steps. -This branch is for Minecraft 26.2. Version `1.1.2.2602002` requires NeoForge -26.2.0.45-beta and Java 25. +This branch is for Minecraft 26.3. Version `1.1.2.2603002` requires NeoForge +26.3.0.1-beta and Java 25. ## Installation @@ -34,7 +34,7 @@ or updating world changing mods. - [Gameplay and recipes](docs/GAMEPLAY.md) - [Configuration](docs/CONFIGURATION.md) - [Upgrading an older world](docs/WORLD-UPGRADES.md) -- [Release notes](docs/RELEASE-1.1.2.2602002.md) +- [Release notes](docs/RELEASE-1.1.2.2603002.md) ## License diff --git a/build.gradle b/build.gradle index 5931021..a102969 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { id 'idea' id 'java-library' id 'maven-publish' - id 'net.neoforged.gradle.userdev' version '7.1.38' + id 'net.neoforged.gradle.userdev' version '7.1.39' } def sha256Of = { File inputFile -> @@ -215,11 +215,11 @@ def releaseFiles = provider { tasks.register('verifyReleaseMetadata') { group = 'verification' doLast { - if (project.minecraft_version != '26.2' || project.neo_version != '26.2.0.45-beta' || + if (project.minecraft_version != '26.3' || project.neo_version != '26.3.0.1-beta' || project.loader_name != 'neoforge' || project.loader_code != '2' || project.java_version != '25' || project.java_toolchain_version != '25.0.3+9' || project.gradle_java_version != '25' || project.curseforge_project_id != '1677588') { - throw new GradleException('Unexpected 26.2 release metadata') + throw new GradleException('Unexpected 26.3 release metadata') } } } @@ -300,8 +300,8 @@ tasks.register('verifyReleaseArtifacts') { String metadata = zip.getInputStream(zip.getEntry('META-INF/neoforge.mods.toml')) .getText(StandardCharsets.UTF_8.name()) - if (!metadata.contains('versionRange="[26.2]"') || - !metadata.contains('versionRange="[26.2.0.45-beta,26.2.1)"')) { + if (!metadata.contains('versionRange="[26.3]"') || + !metadata.contains('versionRange="[26.3.0.1-beta,26.3.1)"')) { throw new GradleException('Packaged NeoForge metadata targets the wrong version') } def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))) diff --git a/docs/RELEASE-1.1.2.2603002.md b/docs/RELEASE-1.1.2.2603002.md new file mode 100644 index 0000000..698b509 --- /dev/null +++ b/docs/RELEASE-1.1.2.2603002.md @@ -0,0 +1,7 @@ +# Sky's Grass Slabs 1.1.2 for Minecraft 26.3 + +This release brings Sky's Grass Slabs to Minecraft 26.3 on NeoForge while +preserving the blocks, recipes, configuration and world data from earlier +versions. + +Requires Minecraft 26.3, NeoForge 26.3.0.1-beta and Java 25. diff --git a/docs/WORLD-UPGRADES.md b/docs/WORLD-UPGRADES.md index 90d3dfa..374e559 100644 --- a/docs/WORLD-UPGRADES.md +++ b/docs/WORLD-UPGRADES.md @@ -6,7 +6,7 @@ world has been checked in game. ## Sky's Grass Slabs content -Install `SkysGrassSlabs-1.1.2.2602002.jar` before opening a world that used an +Install `SkysGrassSlabs-1.1.2.2603002.jar` before opening a world that used an earlier release. The permanent dirt slab, grass slab, path slab and turf IDs are unchanged. @@ -15,7 +15,7 @@ when their chunks are first opened. Old top and bottom slab orientations are retained and begin dry. Keep the mod installed during the first upgraded start so unopened chunks can be converted when they are later visited. -Minecraft 1.13.2 through 26.1.2 worlds already use named block states. Their +Minecraft 1.13.2 through 26.3 worlds already use named block states. Their orientation, snowy and waterlogged states load through Minecraft's normal upgrade process. @@ -69,8 +69,8 @@ When both mods remain installed, their content is left alone by default. See ## Modpack checklist 1. Back up the complete instance and world. -2. Update NeoForge and every required mod to a matching Minecraft 26.2 build. -3. Install the 26.2 Sky's Grass Slabs jar before the first upgraded start. +2. Update NeoForge and every required mod to a matching Minecraft 26.3 build. +3. Install the 26.3 Sky's Grass Slabs jar before the first upgraded start. 4. Read the complete NeoForge missing content list before confirming it. 5. Check representative slabs, turf, paths, inventories and old terrain. 6. Stop and start the upgraded copy once more, then repeat the checks. diff --git a/gradle.properties b/gradle.properties index 2abe3a1..476b8f5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,10 @@ org.gradle.daemon=false org.gradle.configuration-cache=false org.gradle.caching=true org.gradle.parallel=false +# NeoForge 26.3 requires additional heap for Vineflower's TreeFeature pass. +neogradle.subsystems.decompiler.enabled=true +neogradle.subsystems.decompiler.maxMemory=8g +neogradle.subsystems.decompiler.maxThreads=1 # Cross-version release dispatcher metadata loader_name=neoforge @@ -19,14 +23,14 @@ curseforge_project_id=1677588 ## Environment Properties # The Minecraft version must agree with the NeoForge version to get a valid artifact -minecraft_version=26.2 +minecraft_version=26.3 # The Minecraft version range can use any release version of Minecraft as bounds. # Snapshots, pre-releases, and release candidates are not guaranteed to sort properly # as they do not follow standard versioning conventions. -minecraft_version_range=[26.2] -# NeoForge 26.2.0.45-beta targets Minecraft 26.2. -neo_version=26.2.0.45-beta -neo_version_range=[26.2.0.45-beta,26.2.1) +minecraft_version_range=[26.3] +# NeoForge 26.3.0.1-beta targets Minecraft 26.3. +neo_version=26.3.0.1-beta +neo_version_range=[26.3.0.1-beta,26.3.1) ## Mod Properties @@ -39,7 +43,7 @@ mod_name=Sky's Grass Slabs # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-2.1-only # The mod version. See https://semver.org/ -mod_version=1.1.2.2602002 +mod_version=1.1.2.2603002 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/SlabGameTests.java b/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/SlabGameTests.java index 0855ced..28f9ff0 100644 --- a/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/SlabGameTests.java +++ b/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/SlabGameTests.java @@ -30,6 +30,7 @@ import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealSource; import net.minecraft.world.level.block.FenceBlock; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.gamerules.GameRules; @@ -40,13 +41,15 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.neoforged.neoforge.common.Tags; -import net.neoforged.neoforge.common.ItemAbilities; +import net.neoforged.neoforge.common.extensions.IBlockExtension; import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import zone.moddev.mc.skysgrassslabs.SkysGrassSlabs; import zone.moddev.mc.skysgrassslabs.block.DirtSlabBlock; import zone.moddev.mc.skysgrassslabs.block.GrassSlabBlock; import zone.moddev.mc.skysgrassslabs.block.GrassSpread; import zone.moddev.mc.skysgrassslabs.block.PathSlabBlock; +import zone.moddev.mc.skysgrassslabs.block.SlabFlattening; import zone.moddev.mc.skysgrassslabs.block.TurfBlock; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; import zone.moddev.mc.skysgrassslabs.init.ModRecipes; @@ -85,7 +88,6 @@ private static void setClockTime(GameTestHelper helper, int time) { public static void shovelFlatteningPreservesOrientation(GameTestHelper helper) { BlockPos pos = helper.absolutePos(new BlockPos(1, 2, 1)); - UseOnContext context = context(helper, pos, new ItemStack(Items.IRON_SHOVEL)); BlockState dirtTop = ModBlocks.DIRT_SLAB.get().defaultBlockState() .setValue(SlabBlock.TYPE, SlabType.TOP); BlockState grassBottom = ModBlocks.GRASS_SLAB.get().defaultBlockState(); @@ -93,29 +95,35 @@ public static void shovelFlatteningPreservesOrientation(GameTestHelper helper) { BlockState doubled = dirtTop.setValue(SlabBlock.TYPE, SlabType.DOUBLE) .setValue(SlabBlock.WATERLOGGED, false); - BlockState topPath = dirtTop.getToolModifiedState(context, ItemAbilities.SHOVEL_FLATTEN, false); - BlockState bottomPath = grassBottom.getToolModifiedState(context, ItemAbilities.SHOVEL_FLATTEN, false); + Player player = helper.makeMockPlayer(GameType.SURVIVAL); + ItemStack shovel = new ItemStack(Items.IRON_SHOVEL); + player.setItemInHand(InteractionHand.MAIN_HAND, shovel); + + helper.getLevel().setBlock(pos, dirtTop, Block.UPDATE_ALL); + flatten(player, pos); + BlockState topPath = helper.getLevel().getBlockState(pos); require(helper, topPath != null && topPath.is(ModBlocks.PATH_SLAB.get()) && topPath.getValue(SlabBlock.TYPE) == SlabType.TOP, "top orientation was lost"); + + helper.getLevel().setBlock(pos, grassBottom, Block.UPDATE_ALL); + flatten(player, pos); + BlockState bottomPath = helper.getLevel().getBlockState(pos); require(helper, bottomPath != null && bottomPath.is(ModBlocks.PATH_SLAB.get()) && bottomPath.getValue(SlabBlock.TYPE) == SlabType.BOTTOM, "bottom orientation was lost"); - require(helper, waterlogged.getToolModifiedState(context, - ItemAbilities.SHOVEL_FLATTEN, false) == null, "waterlogged dirt flattened"); - BlockState fullPath = doubled.getToolModifiedState(context, ItemAbilities.SHOVEL_FLATTEN, false); + + helper.getLevel().setBlock(pos, waterlogged, Block.UPDATE_ALL); + flatten(player, pos); + require(helper, helper.getLevel().getBlockState(pos).equals(waterlogged), + "waterlogged dirt flattened"); + + helper.getLevel().setBlock(pos, doubled, Block.UPDATE_ALL); + flatten(player, pos); + BlockState fullPath = helper.getLevel().getBlockState(pos); require(helper, fullPath != null && fullPath.is(Blocks.DIRT_PATH), "double dirt slab did not normalize to vanilla path"); - - helper.getLevel().setBlock(pos, dirtTop, Block.UPDATE_ALL); - Player player = helper.makeMockPlayer(GameType.CREATIVE); - ItemStack shovel = new ItemStack(Items.IRON_SHOVEL); - player.setItemInHand(InteractionHand.MAIN_HAND, shovel); - shovel.useOn(new UseOnContext(player, InteractionHand.MAIN_HAND, - new BlockHitResult(Vec3.atCenterOf(pos), Direction.UP, pos, false))); - require(helper, helper.getLevel().getBlockState(pos).is(ModBlocks.PATH_SLAB.get()) - && helper.getLevel().getBlockState(pos).getValue(SlabBlock.TYPE) == SlabType.TOP, - "vanilla shovel use did not create a top path slab"); - require(helper, shovel.getDamageValue() == 1, "shovel durability was not consumed"); + require(helper, shovel.getDamageValue() == 3, + "successful slab flattening did not consume durability exactly once"); helper.succeed(); } @@ -274,15 +282,17 @@ public static void snowPlantsBonemealAndWaterAreTopAware(GameTestHelper helper) BlockState bottom = grass.defaultBlockState(); BlockState top = bottom.setValue(SlabBlock.TYPE, SlabType.TOP); - require(helper, !grass.isValidBonemealTarget(helper.getLevel(), pos, bottom), + require(helper, !grass.isValidBonemealTarget(helper.getLevel(), pos, bottom, + BonemealSource.INTERACTION), "bottom grass slab accepted bonemeal"); - require(helper, grass.isValidBonemealTarget(helper.getLevel(), pos, top), + require(helper, grass.isValidBonemealTarget(helper.getLevel(), pos, top, + BonemealSource.INTERACTION), "top grass slab rejected bonemeal"); require(helper, grass.canSustainPlant(bottom, helper.getLevel(), pos, Direction.UP, - Blocks.DANDELION.defaultBlockState()).isFalse(), + Blocks.DANDELION.defaultBlockState()) == net.minecraft.util.TriState.FALSE, "bottom grass slab sustained a plant"); require(helper, grass.canSustainPlant(top, helper.getLevel(), pos, Direction.UP, - Blocks.DANDELION.defaultBlockState()).isTrue(), + Blocks.DANDELION.defaultBlockState()) == net.minecraft.util.TriState.TRUE, "top grass slab rejected a plant"); helper.getLevel().setBlock(pos, top, Block.UPDATE_ALL); @@ -339,7 +349,7 @@ public static void snowPlantsBonemealAndWaterAreTopAware(GameTestHelper helper) require(helper, Identifier.fromNamespaceAndPath(SkysGrassSlabs.MOD_ID, "path_slab") .equals(BuiltInRegistries.BLOCK.getKey(ModBlocks.PATH_SLAB.get())), "path slab registry ID changed"); - require(helper, BuiltInRegistries.FEATURE.containsKey( + require(helper, BuiltInRegistries.FEATURE_TYPE.containsKey( Identifier.fromNamespaceAndPath( SkysGrassSlabs.MOD_ID, "grass_slab_smoothing")), "worldgen feature registry ID changed"); @@ -360,8 +370,11 @@ public static void turfMatchesPhysicalCarpetAndDropsFromInvalidSoil(GameTestHelp CollisionContext.empty()).bounds().maxY == 1.0D / 16.0D, "turf collision is not one pixel high"); require(helper, !state.hasBlockEntity(), "turf unexpectedly has a block entity"); - require(helper, state.getFlammability(helper.getLevel(), dirtTurf, Direction.UP) == 20 - && state.getFireSpreadSpeed(helper.getLevel(), dirtTurf, Direction.UP) == 60, + IBlockExtension turfExtension = (IBlockExtension) state.getBlock(); + require(helper, turfExtension.getFlammability(state, helper.getLevel(), dirtTurf, + Direction.UP) == 20 + && turfExtension.getFireSpreadSpeed(state, helper.getLevel(), dirtTurf, + Direction.UP) == 60, "turf does not match carpet flammability"); require(helper, !ModBlocks.TURF.get().defaultBlockState().is(BlockTags.WOOL_CARPETS) && !new ItemStack(ModBlocks.TURF_ITEM.get()).is(ItemTags.WOOL_CARPETS), @@ -643,7 +656,7 @@ public static void sheepEatTurfOnceAndRespectMobGriefing(GameTestHelper helper) sheep.setSheared(true); CommonEvents.addTurfEatingGoal(new EntityJoinLevelEvent(sheep, helper.getLevel())); CommonEvents.addTurfEatingGoal(new EntityJoinLevelEvent(sheep, helper.getLevel())); - long goalCount = sheep.goalSelector.getAvailableGoals().stream() + long goalCount = sheep.getGoalSelector().getAvailableGoals().stream() .filter(goal -> goal.getGoal() instanceof TurfEatingGoal).count(); require(helper, goalCount == 1, "sheep received duplicate turf eating goals"); @@ -700,17 +713,20 @@ private static ItemStack useTurfOn(Player player, BlockPos pos) { return turf; } - private static UseOnContext context(GameTestHelper helper, BlockPos pos, ItemStack stack) { - return new UseOnContext(helper.getLevel(), null, InteractionHand.MAIN_HAND, stack, - new BlockHitResult(Vec3.atCenterOf(pos), Direction.UP, pos, false)); - } - private static BlockPlaceContext placeContext(GameTestHelper helper, BlockPos pos, ItemStack stack) { - return new BlockPlaceContext(helper.getLevel(), null, InteractionHand.MAIN_HAND, stack, + Player player = helper.makeMockPlayer(GameType.CREATIVE); + player.setItemInHand(InteractionHand.MAIN_HAND, stack); + return new BlockPlaceContext(player, InteractionHand.MAIN_HAND, stack, new BlockHitResult(Vec3.atCenterOf(pos), Direction.UP, pos, false)); } + private static void flatten(Player player, BlockPos pos) { + SlabFlattening.handle(new PlayerInteractEvent.RightClickBlock(player, + InteractionHand.MAIN_HAND, pos, + new BlockHitResult(Vec3.atCenterOf(pos), Direction.UP, pos, false))); + } + private static void require(GameTestHelper helper, boolean condition, String message) { if (!condition) { helper.fail(message); diff --git a/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/WorldgenGameTests.java b/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/WorldgenGameTests.java index 7676eb2..46e97ea 100644 --- a/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/WorldgenGameTests.java +++ b/src/gameTest/java/zone/moddev/mc/skysgrassslabs/gametest/WorldgenGameTests.java @@ -1,6 +1,6 @@ package zone.moddev.mc.skysgrassslabs.gametest; -import java.util.Optional; +import java.util.Set; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.resources.Identifier; @@ -9,11 +9,8 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import net.neoforged.neoforge.common.world.BiomeModifier; -import net.neoforged.neoforge.common.world.ModifiableBiomeInfo.BiomeInfo; import net.neoforged.neoforge.registries.NeoForgeRegistries; import zone.moddev.mc.skysgrassslabs.SkysGrassSlabs; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; @@ -34,14 +31,11 @@ public static void smoothingIsFirstVegetationFeature(GameTestHelper helper) { require(helper, modifier instanceof zone.moddev.mc.skysgrassslabs.world.SmoothingBiomeModifier, "registered smoothing biome modifier is missing"); - BiomeInfo.Builder builder = BiomeInfo.Builder.copyOf( - biome.value().modifiableBiomeInfo().getOriginalBiomeInfo()); - modifier.modify(biome, BiomeModifier.Phase.AFTER_EVERYTHING, builder); - var vegetation = builder.build().generationSettings().features() + var vegetation = biome.value().getGenerationSettings().features() .get(GenerationStep.Decoration.VEGETAL_DECORATION.ordinal()); require(helper, vegetation.size() > 0, "biome has no vegetation features"); PlacedFeature first = vegetation.get(0).value(); - require(helper, first.feature().value().feature() instanceof GrassSlabSmoothingFeature, + require(helper, first.feature().value() instanceof GrassSlabSmoothingFeature, "smoother is not the first vegetation feature"); helper.succeed(); } @@ -70,14 +64,12 @@ public static void smoothingIsBorderSafeAndIdempotent(GameTestHelper helper) { helper.getLevel().setBlock(chest, Blocks.CHEST.defaultBlockState(), Block.UPDATE_ALL); helper.runAfterDelay(5, () -> { - GrassSlabSmoothingFeature feature = new GrassSlabSmoothingFeature( - NoneFeatureConfiguration.CODEC); - FeaturePlaceContext context = new FeaturePlaceContext<>( - Optional.empty(), helper.getLevel(), + primeLoadedHeightmaps(helper, owner); + GrassSlabSmoothingFeature feature = new GrassSlabSmoothingFeature(); + require(helper, feature.place(helper.getLevel(), helper.getLevel().getChunkSource().getGenerator(), - RandomSource.create(19780401L), - center, NoneFeatureConfiguration.INSTANCE); - require(helper, feature.place(context), "controlled feature pass made no changes"); + RandomSource.create(19780401L), center), + "controlled feature pass made no changes"); require(helper, helper.getLevel().getBlockState(center.above()) .is(ModBlocks.GRASS_SLAB.get()), "one-block transition was not smoothed"); require(helper, helper.getLevel().getBlockState(border.above()) @@ -95,7 +87,9 @@ public static void smoothingIsBorderSafeAndIdempotent(GameTestHelper helper) { "block entity was overwritten"); int firstCount = countGrassSlabs(helper, owner); helper.runAfterDelay(10, () -> { - boolean secondChanged = feature.place(context); + boolean secondChanged = feature.place(helper.getLevel(), + helper.getLevel().getChunkSource().getGenerator(), + RandomSource.create(19780401L), center); int secondCount = countGrassSlabs(helper, owner); require(helper, !secondChanged && secondCount == firstCount, "settled second pass changed output: before=" + firstCount @@ -105,6 +99,17 @@ public static void smoothingIsBorderSafeAndIdempotent(GameTestHelper helper) { }); } + private static void primeLoadedHeightmaps(GameTestHelper helper, ChunkPos owner) { + for (int chunkX = owner.x() - 1; chunkX <= owner.x() + 1; chunkX++) { + for (int chunkZ = owner.z() - 1; chunkZ <= owner.z() + 1; chunkZ++) { + var chunk = helper.getLevel().getChunkSource().getChunkNow(chunkX, chunkZ); + if (chunk != null) { + Heightmap.primeHeightmaps(chunk, Set.of(Heightmap.Types.WORLD_SURFACE_WG)); + } + } + } + } + private static int countGrassSlabs(GameTestHelper helper, ChunkPos chunk) { int count = 0; BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos(); diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java b/src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java index 7777e52..7fa1558 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java @@ -27,7 +27,7 @@ public final class SkysGrassSlabs { /** Stable mod identifier and resource namespace. */ public static final String MOD_ID = "skysgrassslabs"; public static final String NAME = "Sky's Grass Slabs"; - public static final String VERSION = "1.1.2.2602002"; + public static final String VERSION = "1.1.2.2603002"; public static final Logger LOGGER = LogManager.getLogger(); /** Registers content, configuration, world generation, and persistent state. */ diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/DirtSlabBlock.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/DirtSlabBlock.java index e9f5325..8839c04 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/block/DirtSlabBlock.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/DirtSlabBlock.java @@ -6,7 +6,6 @@ import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; import net.minecraft.world.level.block.Block; @@ -14,7 +13,6 @@ import net.minecraft.world.level.block.SnowyBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; -import net.neoforged.neoforge.common.ItemAbility; /** Dirt slab with snow presentation and target-aware grass growth. */ public final class DirtSlabBlock extends SlabBlock { @@ -57,10 +55,4 @@ public void randomTick(BlockState state, ServerLevel level, BlockPos pos, Random GrassSpread.tickDirtSlab(level, pos, repaired, random); } - @Override - @Nullable - public BlockState getToolModifiedState(BlockState state, UseOnContext context, - ItemAbility action, boolean simulate) { - return SlabTransitions.flatten(state, action); - } } diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/GrassSlabBlock.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/GrassSlabBlock.java index 1b27e7b..31c3d5b 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/block/GrassSlabBlock.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/GrassSlabBlock.java @@ -15,13 +15,13 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealSource; import net.minecraft.world.level.block.BonemealableBlock; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.SnowyBlock; @@ -29,13 +29,14 @@ import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.SlabType; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import net.neoforged.neoforge.common.ItemAbility; +import net.neoforged.neoforge.common.extensions.IBlockExtension; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; /** Grass slab with target aware spreading and top slab vegetation behaviour. */ -public final class GrassSlabBlock extends SlabBlock implements BonemealableBlock { +public final class GrassSlabBlock extends SlabBlock + implements BonemealableBlock, IBlockExtension { public static final BooleanProperty SNOWY = SnowyBlock.SNOWY; public GrassSlabBlock(Properties properties) { @@ -102,14 +103,6 @@ public void randomTick(BlockState state, ServerLevel level, BlockPos pos, Random GrassSpread.spreadFrom(level, pos, random, pos.below()); } - @Override - @Nullable - public BlockState getToolModifiedState(BlockState state, UseOnContext context, - ItemAbility action, boolean simulate) { - - return SlabTransitions.flatten(state, action); - } - @Override public TriState canSustainPlant(BlockState state, BlockGetter level, BlockPos pos, Direction direction, BlockState plant) { @@ -122,19 +115,22 @@ public TriState canSustainPlant(BlockState state, BlockGetter level, BlockPos po } @Override - public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) { + public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state, + BonemealSource source) { return state.getValue(TYPE) == SlabType.TOP && !state.getValue(WATERLOGGED) && level.getBlockState(pos.above()).isAir(); } @Override - public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { + public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, + BlockState state, BonemealSource source) { return state.getValue(TYPE) == SlabType.TOP && !state.getValue(WATERLOGGED); } @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - if (!isBonemealSuccess(level, random, pos, state)) { + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, + BlockState state, BonemealSource source) { + if (!isBonemealSuccess(level, random, pos, state, source)) { return; } @@ -167,19 +163,19 @@ public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos if (current.is(vanillaGrass.getBlock()) && random.nextInt(10) == 0) { ((BonemealableBlock) vanillaGrass.getBlock()).performBonemeal(level, random, - target, current); + target, current, source); } if (current.isAir()) { if (random.nextInt(8) == 0) { - List> flowers = level.getBiome(target).value() + List flowers = level.getBiome(target).value() .getGenerationSettings().getBoneMealFeatures(); if (flowers.isEmpty()) { continue; } - ConfiguredFeature flower = flowers.get(random.nextInt(flowers.size())); + Feature flower = flowers.get(random.nextInt(flowers.size())); flower.place(level, level.getChunkSource().getGenerator(), random, target); } else if (grassFeature.isPresent()) { grassFeature.get().value().place(level, diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabFlattening.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabFlattening.java new file mode 100644 index 0000000..20f55fd --- /dev/null +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabFlattening.java @@ -0,0 +1,65 @@ +package zone.moddev.mc.skysgrassslabs.block; + +import net.minecraft.advancements.triggers.CriteriaTriggers; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; +import zone.moddev.mc.skysgrassslabs.init.ModBlocks; +import zone.moddev.mc.skysgrassslabs.item.ShovelSupport; + +/** Orientation-aware slab flattening for the data-driven shovel contract. */ +public final class SlabFlattening { + private SlabFlattening() { + } + + public static void handle(PlayerInteractEvent.RightClickBlock event) { + ItemStack stack = event.getItemStack(); + if (!ShovelSupport.isShovel(stack) || event.getFace() == Direction.DOWN) { + return; + } + + Level level = event.getLevel(); + BlockPos pos = event.getPos(); + BlockState current = level.getBlockState(pos); + if ((!current.is(ModBlocks.DIRT_SLAB.get()) && !current.is(ModBlocks.GRASS_SLAB.get())) + || !level.getBlockState(pos.above()).isAir()) { + return; + } + + var player = event.getEntity(); + if (!level.mayInteract(player, pos) + || !player.mayUseItemAt(pos, event.getFace(), stack) + || event.getHand() == InteractionHand.MAIN_HAND + && player.getOffhandItem().has(DataComponents.BLOCKS_ATTACKS) + && !player.isSecondaryUseActive()) { + return; + } + + BlockState flattened = SlabTransitions.flatten(current); + if (flattened == null) { + return; + } + + if (player instanceof ServerPlayer serverPlayer) { + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(serverPlayer, pos, stack); + } + stack.hurtAndBreak(1, player, event.getHand().asEquipmentSlot()); + level.setBlock(pos, flattened, Block.UPDATE_ALL_IMMEDIATE); + level.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN.value(), SoundSource.BLOCKS, + 1.0F, 1.0F); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, flattened)); + event.setCancellationResult(InteractionResult.SUCCESS); + event.setCanceled(true); + } +} diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabTransitions.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabTransitions.java index ffa0b24..fc51d2e 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabTransitions.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/SlabTransitions.java @@ -6,8 +6,6 @@ import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.SlabType; -import net.neoforged.neoforge.common.ItemAbility; -import net.neoforged.neoforge.common.ItemAbilities; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; /** Lossless state transitions shared by soil slabs. */ @@ -16,8 +14,8 @@ private SlabTransitions() { } @Nullable - static BlockState flatten(BlockState state, ItemAbility action) { - if (action != ItemAbilities.SHOVEL_FLATTEN || state.getValue(SlabBlock.WATERLOGGED)) { + static BlockState flatten(BlockState state) { + if (state.getValue(SlabBlock.WATERLOGGED)) { return null; } diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlock.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlock.java index 571cdc5..e96208d 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlock.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlock.java @@ -12,9 +12,10 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CarpetBlock; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.extensions.IBlockExtension; /** Biome tinted grass source at carpet height with no dirt state. */ -public final class TurfBlock extends CarpetBlock { +public final class TurfBlock extends CarpetBlock implements IBlockExtension { public TurfBlock(Properties properties) { super(properties); } diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlockItem.java b/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlockItem.java index 3f9571e..fa4c7a2 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlockItem.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/block/TurfBlockItem.java @@ -56,7 +56,7 @@ public InteractionResult useOn(UseOnContext context) { CriteriaTriggers.PLACED_BLOCK.trigger(serverPlayer, pos, held); } - var sound = grass.getSoundType(level, pos, player); + var sound = grass.getSoundType(); level.playSound(player, pos, sound.getPlaceSound(), SoundSource.BLOCKS, (sound.getVolume() + 1.0F) / 2.0F, sound.getPitch() * 0.8F); diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/event/CommonEvents.java b/src/main/java/zone/moddev/mc/skysgrassslabs/event/CommonEvents.java index 3f94b75..42bbca3 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/event/CommonEvents.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/event/CommonEvents.java @@ -6,6 +6,8 @@ import net.minecraft.world.entity.animal.sheep.Sheep; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; +import zone.moddev.mc.skysgrassslabs.block.SlabFlattening; import zone.moddev.mc.skysgrassslabs.entity.ai.TurfEatingGoal; /** Server gameplay event registrations. */ @@ -15,6 +17,7 @@ public final class CommonEvents { public static void register() { NeoForge.EVENT_BUS.addListener(CommonEvents::addTurfEatingGoal); + NeoForge.EVENT_BUS.addListener(CommonEvents::flattenSlab); } public static void addTurfEatingGoal(EntityJoinLevelEvent event) { @@ -22,10 +25,14 @@ public static void addTurfEatingGoal(EntityJoinLevelEvent event) { return; } if (TURF_GOAL_SHEEP.add(sheep)) { - sheep.goalSelector.addGoal(5, new TurfEatingGoal(sheep)); + sheep.getGoalSelector().addGoal(5, new TurfEatingGoal(sheep)); } } + private static void flattenSlab(PlayerInteractEvent.RightClickBlock event) { + SlabFlattening.handle(event); + } + private CommonEvents() { } } diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/init/ModBlocks.java b/src/main/java/zone/moddev/mc/skysgrassslabs/init/ModBlocks.java index ad72315..fc30343 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/init/ModBlocks.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/init/ModBlocks.java @@ -2,7 +2,7 @@ import net.minecraft.world.item.BlockItem; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.item.CreativeModeTabs; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -25,6 +25,8 @@ /** Stable block and item registrations. */ @EventBusSubscriber(modid = SkysGrassSlabs.MOD_ID) public final class ModBlocks { + private static final Identifier BUILDING_BLOCKS_TAB = + Identifier.withDefaultNamespace("building_blocks"); public static final DeferredRegister BLOCKS = DeferredRegister.create(BuiltInRegistries.BLOCK, SkysGrassSlabs.MOD_ID); public static final DeferredRegister ITEMS = @@ -91,7 +93,7 @@ private static DeferredHolder slabItem(String name, DeferredHolder transformer = stack.get(DataComponents.BLOCK_TRANSFORMER); + return transformer != null && transformer.is(BlockTransformers.SHOVEL); + } +} diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/recipe/TurfCuttingRecipe.java b/src/main/java/zone/moddev/mc/skysgrassslabs/recipe/TurfCuttingRecipe.java index a8daad4..cb8cbe1 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/recipe/TurfCuttingRecipe.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/recipe/TurfCuttingRecipe.java @@ -19,10 +19,10 @@ import net.minecraft.world.item.crafting.display.ShapelessCraftingRecipeDisplay; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; -import net.neoforged.neoforge.common.ItemAbilities; import zone.moddev.mc.skysgrassslabs.compat.BuildingBricksCompat; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; import zone.moddev.mc.skysgrassslabs.init.ModRecipes; +import zone.moddev.mc.skysgrassslabs.item.ShovelSupport; /** Cuts turf while returning the matching dirt and an unchanged shovel. */ public final class TurfCuttingRecipe extends CustomRecipe { @@ -121,7 +121,7 @@ public RecipeSerializer getSerializer() { } private static boolean isShovel(ItemStack stack) { - return stack.canPerformAction(ItemAbilities.SHOVEL_FLATTEN); + return ShovelSupport.isShovel(stack); } private static NonNullList createIngredients() { diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/world/GrassSlabSmoothingFeature.java b/src/main/java/zone/moddev/mc/skysgrassslabs/world/GrassSlabSmoothingFeature.java index 30e6c35..326edb2 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/world/GrassSlabSmoothingFeature.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/world/GrassSlabSmoothingFeature.java @@ -1,6 +1,6 @@ package zone.moddev.mc.skysgrassslabs.world; -import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; import java.util.Arrays; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; @@ -14,35 +14,38 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.util.RandomSource; import zone.moddev.mc.skysgrassslabs.compat.LegacyWorldDataHook; import zone.moddev.mc.skysgrassslabs.config.SkysGrassSlabsConfig; import zone.moddev.mc.skysgrassslabs.init.ModBlocks; /** Deterministic two-pass slope smoothing for newly generated Overworld chunks. */ -public final class GrassSlabSmoothingFeature extends Feature { +public final class GrassSlabSmoothingFeature implements Feature { + public static final MapCodec CODEC = + MapCodec.unit(GrassSlabSmoothingFeature::new); private static final int HALO_WIDTH = 18; private static final int MISSING = Integer.MIN_VALUE; private static final ThreadLocal BUFFERS = ThreadLocal.withInitial(DecisionBuffer::new); - public GrassSlabSmoothingFeature(Codec codec) { - super(codec); + @Override + public MapCodec codec() { + return CODEC; } @Override - public boolean place(FeaturePlaceContext context) { - WorldGenLevel level = context.level(); + public boolean place(WorldGenLevel level, ChunkGenerator generator, RandomSource random, + BlockPos origin) { if (!SkysGrassSlabsConfig.isSmoothingActive() || level.getLevel().dimension() != Level.OVERWORLD) { return false; } ChunkAccess owner = level instanceof WorldGenRegion region ? level.getChunk(region.getCenter().x(), region.getCenter().z()) - : level.getChunk(context.origin()); + : level.getChunk(origin); ChunkPos ownerPos = owner.getPos(); if (LegacyWorldDataHook.isLegacyChunk(ownerPos.x(), ownerPos.z())) { return false; diff --git a/src/main/java/zone/moddev/mc/skysgrassslabs/world/WorldgenBootstrap.java b/src/main/java/zone/moddev/mc/skysgrassslabs/world/WorldgenBootstrap.java index eee18fd..df0ff49 100644 --- a/src/main/java/zone/moddev/mc/skysgrassslabs/world/WorldgenBootstrap.java +++ b/src/main/java/zone/moddev/mc/skysgrassslabs/world/WorldgenBootstrap.java @@ -4,9 +4,7 @@ import java.util.Collections; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.neoforged.neoforge.common.world.BiomeModifier; import net.neoforged.bus.api.IEventBus; @@ -20,14 +18,14 @@ public final class WorldgenBootstrap { public static final String FEATURE_NAME = "grass_slab_smoothing"; - private static final DeferredRegister> FEATURES = - DeferredRegister.create(BuiltInRegistries.FEATURE, SkysGrassSlabs.MOD_ID); + private static final DeferredRegister> FEATURE_TYPES = + DeferredRegister.create(BuiltInRegistries.FEATURE_TYPE, SkysGrassSlabs.MOD_ID); private static final DeferredRegister> BIOME_MODIFIERS = DeferredRegister.create(NeoForgeRegistries.BIOME_MODIFIER_SERIALIZERS, SkysGrassSlabs.MOD_ID); - private static final DeferredHolder, GrassSlabSmoothingFeature> SMOOTHING = - FEATURES.register(FEATURE_NAME, - () -> new GrassSlabSmoothingFeature(NoneFeatureConfiguration.CODEC)); + private static final DeferredHolder, + MapCodec> SMOOTHING = + FEATURE_TYPES.register(FEATURE_NAME, () -> GrassSlabSmoothingFeature.CODEC); private static volatile Holder placedFeature; @@ -39,7 +37,7 @@ private WorldgenBootstrap() { } public static void register(IEventBus modBus) { - FEATURES.register(modBus); + FEATURE_TYPES.register(modBus); BIOME_MODIFIERS.register(modBus); modBus.addListener(WorldgenBootstrap::commonSetup); } @@ -50,10 +48,8 @@ private static void commonSetup(FMLCommonSetupEvent event) { static synchronized Holder placedFeature() { if (placedFeature == null) { - Holder> configured = Holder.direct( - new ConfiguredFeature( - SMOOTHING.get(), NoneFeatureConfiguration.INSTANCE)); - placedFeature = Holder.direct(new PlacedFeature(configured, Collections.emptyList())); + Holder feature = Holder.direct(new GrassSlabSmoothingFeature()); + placedFeature = Holder.direct(new PlacedFeature(feature, Collections.emptyList())); } return placedFeature; } diff --git a/src/main/resources/data/skysgrassslabs/advancement/recipes/dirt_slab.json b/src/main/resources/data/skysgrassslabs/advancement/recipes/dirt_slab.json index 52b8d50..9b28bbd 100644 --- a/src/main/resources/data/skysgrassslabs/advancement/recipes/dirt_slab.json +++ b/src/main/resources/data/skysgrassslabs/advancement/recipes/dirt_slab.json @@ -8,7 +8,7 @@ }, "has_the_recipe": { "trigger": "minecraft:recipe_unlocked", - "conditions": { "recipe": "skysgrassslabs:dirt_slab" } + "conditions": { "recipes": "skysgrassslabs:dirt_slab" } } }, "requirements": [["has_dirt", "has_the_recipe"]] diff --git a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_block_from_seeds.json b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_block_from_seeds.json index 12c8b69..11f1e1a 100644 --- a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_block_from_seeds.json +++ b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_block_from_seeds.json @@ -8,7 +8,7 @@ }, "has_the_recipe": { "trigger": "minecraft:recipe_unlocked", - "conditions": { "recipe": "skysgrassslabs:grass_block_from_seeds" } + "conditions": { "recipes": "skysgrassslabs:grass_block_from_seeds" } } }, "requirements": [["has_dirt", "has_the_recipe"]] diff --git a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab.json b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab.json index c0234a0..f7f0d92 100644 --- a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab.json +++ b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab.json @@ -8,7 +8,7 @@ }, "has_the_recipe": { "trigger": "minecraft:recipe_unlocked", - "conditions": { "recipe": "skysgrassslabs:grass_slab" } + "conditions": { "recipes": "skysgrassslabs:grass_slab" } } }, "requirements": [["has_grass_block", "has_the_recipe"]] diff --git a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab_from_seeds.json b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab_from_seeds.json index cb5adc0..615e35e 100644 --- a/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab_from_seeds.json +++ b/src/main/resources/data/skysgrassslabs/advancement/recipes/grass_slab_from_seeds.json @@ -8,7 +8,7 @@ }, "has_the_recipe": { "trigger": "minecraft:recipe_unlocked", - "conditions": { "recipe": "skysgrassslabs:grass_slab_from_seeds" } + "conditions": { "recipes": "skysgrassslabs:grass_slab_from_seeds" } } }, "requirements": [["has_dirt_slab", "has_the_recipe"]] diff --git a/src/main/resources/data/skysgrassslabs/advancement/recipes/turf.json b/src/main/resources/data/skysgrassslabs/advancement/recipes/turf.json index 125a996..7fbfbcf 100644 --- a/src/main/resources/data/skysgrassslabs/advancement/recipes/turf.json +++ b/src/main/resources/data/skysgrassslabs/advancement/recipes/turf.json @@ -12,7 +12,7 @@ }, "has_the_recipe": { "trigger": "minecraft:recipe_unlocked", - "conditions": { "recipe": "skysgrassslabs:turf" } + "conditions": { "recipes": "skysgrassslabs:turf" } } }, "requirements": [["has_grass_block", "has_grass_slab", "has_the_recipe"]] diff --git a/src/main/resources/data/skysgrassslabs/loot_table/blocks/dirt_slab.json b/src/main/resources/data/skysgrassslabs/loot_table/blocks/dirt_slab.json index 90bc9a2..c4a36e3 100644 --- a/src/main/resources/data/skysgrassslabs/loot_table/blocks/dirt_slab.json +++ b/src/main/resources/data/skysgrassslabs/loot_table/blocks/dirt_slab.json @@ -1,24 +1,27 @@ { "type": "minecraft:block", - "pools": [{ - "rolls": 1.0, - "bonus_rolls": 0.0, - "entries": [{ - "type": "minecraft:item", - "name": "skysgrassslabs:dirt_slab", - "functions": [ + "pools": [ + { + "entries": [ { - "function": "minecraft:set_count", - "conditions": [{ - "condition": "minecraft:block_state_property", - "block": "skysgrassslabs:dirt_slab", - "properties": { "type": "double" } - }], - "count": 2.0, - "add": false - }, - { "function": "minecraft:explosion_decay" } - ] - }] - }] + "type": "minecraft:item", + "modifier": [ + { + "type": "minecraft:set_count", + "condition": { + "type": "minecraft:match_block", + "blocks": "skysgrassslabs:dirt_slab", + "state": { "type": "double" } + }, + "count": 2 + }, + { "type": "minecraft:explosion_decay" } + ], + "name": "skysgrassslabs:dirt_slab" + } + ], + "rolls": 1 + } + ], + "random_sequence": "skysgrassslabs:blocks/dirt_slab" } diff --git a/src/main/resources/data/skysgrassslabs/loot_table/blocks/grass_slab.json b/src/main/resources/data/skysgrassslabs/loot_table/blocks/grass_slab.json index 2224f73..fa3aa1e 100644 --- a/src/main/resources/data/skysgrassslabs/loot_table/blocks/grass_slab.json +++ b/src/main/resources/data/skysgrassslabs/loot_table/blocks/grass_slab.json @@ -1,52 +1,48 @@ { "type": "minecraft:block", - "pools": [{ - "rolls": 1.0, - "bonus_rolls": 0.0, - "entries": [{ - "type": "minecraft:alternatives", - "children": [ + "pools": [ + { + "entries": [ { - "type": "minecraft:item", - "name": "skysgrassslabs:grass_slab", - "conditions": [{ - "condition": "minecraft:match_tool", - "predicate": { - "predicates": { - "minecraft:enchantments": [{ - "enchantments": "minecraft:silk_touch", - "levels": { "min": 1 } - }] - } + "type": "minecraft:alternatives", + "children": [ + { + "type": "minecraft:item", + "condition": "minecraft:tool/can_silk_touch", + "modifier": [ + { + "type": "minecraft:set_count", + "condition": { + "type": "minecraft:match_block", + "blocks": "skysgrassslabs:grass_slab", + "state": { "type": "double" } + }, + "count": 2 + } + ], + "name": "skysgrassslabs:grass_slab" + }, + { + "type": "minecraft:item", + "condition": { "type": "minecraft:survives_explosion" }, + "modifier": [ + { + "type": "minecraft:set_count", + "condition": { + "type": "minecraft:match_block", + "blocks": "skysgrassslabs:grass_slab", + "state": { "type": "double" } + }, + "count": 2 + } + ], + "name": "skysgrassslabs:dirt_slab" } - }], - "functions": [{ - "function": "minecraft:set_count", - "conditions": [{ - "condition": "minecraft:block_state_property", - "block": "skysgrassslabs:grass_slab", - "properties": { "type": "double" } - }], - "count": 2.0, - "add": false - }] - }, - { - "type": "minecraft:item", - "name": "skysgrassslabs:dirt_slab", - "conditions": [{ "condition": "minecraft:survives_explosion" }], - "functions": [{ - "function": "minecraft:set_count", - "conditions": [{ - "condition": "minecraft:block_state_property", - "block": "skysgrassslabs:grass_slab", - "properties": { "type": "double" } - }], - "count": 2.0, - "add": false - }] + ] } - ] - }] - }] + ], + "rolls": 1 + } + ], + "random_sequence": "skysgrassslabs:blocks/grass_slab" } diff --git a/src/main/resources/data/skysgrassslabs/loot_table/blocks/path_slab.json b/src/main/resources/data/skysgrassslabs/loot_table/blocks/path_slab.json index 7cd841e..687f919 100644 --- a/src/main/resources/data/skysgrassslabs/loot_table/blocks/path_slab.json +++ b/src/main/resources/data/skysgrassslabs/loot_table/blocks/path_slab.json @@ -1,22 +1,27 @@ { "type": "minecraft:block", - "pools": [{ - "rolls": 1.0, - "bonus_rolls": 0.0, - "entries": [{ - "type": "minecraft:item", - "name": "skysgrassslabs:dirt_slab", - "conditions": [{ "condition": "minecraft:survives_explosion" }], - "functions": [{ - "function": "minecraft:set_count", - "conditions": [{ - "condition": "minecraft:block_state_property", - "block": "skysgrassslabs:path_slab", - "properties": { "type": "double" } - }], - "count": 2.0, - "add": false - }] - }] - }] + "pools": [ + { + "entries": [ + { + "type": "minecraft:item", + "condition": { "type": "minecraft:survives_explosion" }, + "modifier": [ + { + "type": "minecraft:set_count", + "condition": { + "type": "minecraft:match_block", + "blocks": "skysgrassslabs:path_slab", + "state": { "type": "double" } + }, + "count": 2 + } + ], + "name": "skysgrassslabs:dirt_slab" + } + ], + "rolls": 1 + } + ], + "random_sequence": "skysgrassslabs:blocks/path_slab" } diff --git a/src/main/resources/data/skysgrassslabs/loot_table/blocks/turf.json b/src/main/resources/data/skysgrassslabs/loot_table/blocks/turf.json index 8c5d9a2..5b06db2 100644 --- a/src/main/resources/data/skysgrassslabs/loot_table/blocks/turf.json +++ b/src/main/resources/data/skysgrassslabs/loot_table/blocks/turf.json @@ -2,13 +2,12 @@ "type": "minecraft:block", "pools": [ { - "rolls": 1, + "condition": { "type": "minecraft:survives_explosion" }, "entries": [ { "type": "minecraft:item", "name": "skysgrassslabs:turf" } ], - "conditions": [ - { "condition": "minecraft:survives_explosion" } - ] + "rolls": 1 } - ] + ], + "random_sequence": "skysgrassslabs:blocks/turf" } diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 9b39dd9..3cfe599 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,9 +1,9 @@ { "pack": { "description": "${mod_id} resources", - "max_format": 107, + "max_format": 121, "min_format": [ - 107, + 97, 1 ] } diff --git a/src/test/java/zone/moddev/mc/skysgrassslabs/ProjectContractTest.java b/src/test/java/zone/moddev/mc/skysgrassslabs/ProjectContractTest.java index ea50663..14c56fe 100644 --- a/src/test/java/zone/moddev/mc/skysgrassslabs/ProjectContractTest.java +++ b/src/test/java/zone/moddev/mc/skysgrassslabs/ProjectContractTest.java @@ -18,9 +18,10 @@ public void metadataUsesStableIdentityWithoutOreSpawnDependency() throws Excepti String properties = Files.readString(Path.of("gradle.properties"), StandardCharsets.UTF_8); assertTrue(properties.contains("mod_id=skysgrassslabs")); - assertTrue(properties.contains("minecraft_version=26.2")); - assertTrue(properties.contains("neo_version=26.2.0.45-beta")); - assertFalse(properties.contains("neogradle.subsystems.decompiler.enabled")); + assertTrue(properties.contains("minecraft_version=26.3")); + assertTrue(properties.contains("neo_version=26.3.0.1-beta")); + assertTrue(properties.contains("neogradle.subsystems.decompiler.enabled=true")); + assertTrue(properties.contains("neogradle.subsystems.decompiler.maxMemory=8g")); assertTrue(properties.contains("java_toolchain_version=25.0.3+9")); String build = Files.readString(Path.of("build.gradle"), StandardCharsets.UTF_8); assertTrue(build.contains("verifyJava25Toolchain")); @@ -34,14 +35,14 @@ public void playerAndMaintainerDocumentsExist() { assertTrue(Files.isRegularFile(Path.of("docs/GAMEPLAY.md"))); assertTrue(Files.isRegularFile(Path.of("docs/WORLD-UPGRADES.md"))); assertFalse(Files.exists(Path.of("docs/REPOSITORY.md"))); - assertTrue(Files.isRegularFile(Path.of("docs/RELEASE-1.1.2.2602002.md"))); + assertTrue(Files.isRegularFile(Path.of("docs/RELEASE-1.1.2.2603002.md"))); assertTrue(Files.isRegularFile(Path.of("docs/BETA-0.2.0.118021.md"))); } @Test public void releaseIdentityAndLicenseAreStable() throws Exception { String properties = Files.readString(Path.of("gradle.properties"), StandardCharsets.UTF_8); - assertTrue(properties.contains("mod_version=1.1.2.2602002")); + assertTrue(properties.contains("mod_version=1.1.2.2603002")); assertTrue(properties.contains("mod_license=LGPL-2.1-only")); assertEquals("LGPL-2.1-only", Files.readString(Path.of("LICENSE.spdx"), StandardCharsets.UTF_8).trim()); assertTrue(Files.readString(Path.of("NOTICE"), StandardCharsets.UTF_8) @@ -63,7 +64,7 @@ public void releaseDispatcherIsExplicitAndUsesTheImmutableBundle() throws Except digits = digits.substring(0, digits.length() - 2); int minor = Integer.parseInt(digits.substring(digits.length() - 2)); int major = Integer.parseInt(digits.substring(0, digits.length() - 2)); - assertEquals("master-26.2", "master-" + major + "." + minor + assertEquals("master-26.3", "master-" + major + "." + minor + (patch == 0 ? "" : "." + patch)); assertTrue(workflow.contains("target_suffix=\"${BASH_REMATCH[1]}\"")); assertTrue(workflow.contains("\"master-$mc_major.$mc_minor.$mc_patch$loader_suffix\"")); @@ -116,7 +117,7 @@ public void commonConfigAndWorldStateUsePermanentKeys() throws Exception { assertTrue(config.contains("push(\"compat\")")); assertTrue(config.contains("define(FORCE_REPLACE_BUILDINGBRICKS_SLABS, false)")); assertTrue(config.contains("define(FORCE_REPLACE_GRASS_SLABS_MOD_CONTENT, false)")); - assertTrue(main.contains("VERSION = \"1.1.2.2602002\"")); + assertTrue(main.contains("VERSION = \"1.1.2.2603002\"")); assertTrue(state.contains("skysgrassslabs_world_state")); assertTrue(state.contains("SCHEMA_VERSION = 1")); assertTrue(state.contains("schema_version")); @@ -223,7 +224,12 @@ public void acceptedRenderingAndRecipeContractsRemainVisible() throws Exception assertTrue(recipe.contains("MapCodec")); assertTrue(recipe.contains("StreamCodec")); assertTrue(recipe.contains("return false;")); - assertTrue(recipe.contains("canPerformAction(ItemAbilities.SHOVEL_FLATTEN)")); + assertTrue(recipe.contains("ShovelSupport.isShovel(stack)")); + String shovelSupport = Files.readString(Path.of( + "src/main/java/zone/moddev/mc/skysgrassslabs/item/ShovelSupport.java"), + StandardCharsets.UTF_8); + assertTrue(shovelSupport.contains("DataComponents.BLOCK_TRANSFORMER")); + assertTrue(shovelSupport.contains("BlockTransformers.SHOVEL")); assertFalse(client.contains("ItemBlockRenderTypes")); for (String modelName : new String[] {"grass_slab", "grass_slab_top", "grass_slab_snow", "grass_slab_top_snow"}) { @@ -236,7 +242,7 @@ public void acceptedRenderingAndRecipeContractsRemainVisible() throws Exception } @Test - public void neoForge262LifecycleAndDataPackContractsArePresent() throws Exception { + public void neoForge263LifecycleAndDataPackContractsArePresent() throws Exception { String blocks = Files.readString(Path.of( "src/main/java/zone/moddev/mc/skysgrassslabs/init/ModBlocks.java"), StandardCharsets.UTF_8); @@ -261,9 +267,9 @@ public void neoForge262LifecycleAndDataPackContractsArePresent() throws Exceptio assertTrue(modifier.contains("BiomeTags.IS_END")); assertTrue(modifierJson.contains("skysgrassslabs:grass_slab_smoothing")); assertTrue(worldgen.contains("DeferredRegister>")); - assertTrue(pack.contains("\"max_format\": 107")); + assertTrue(pack.contains("\"max_format\": 121")); assertTrue(pack.contains("\"min_format\":")); - assertTrue(pack.contains("107")); + assertTrue(pack.contains("97")); for (String recipeName : new String[] {"dirt_slab", "grass_slab", "grass_block_from_seeds", "grass_slab_from_seeds", "turf"}) { @@ -292,13 +298,13 @@ public void continuousIntegrationTargetsTheStableBranchAndArtifacts() throws Exc "validate-gradle-build.yml"}) { String workflow = Files.readString(Path.of(".github/workflows", name), StandardCharsets.UTF_8); - assertTrue(name, workflow.contains("master-26.2-neo")); + assertTrue(name, workflow.contains("master-26.3-neo")); } String ci = Files.readString(Path.of(".github/workflows/ci.yml"), StandardCharsets.UTF_8); - assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2602002.jar")); - assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2602002-sources.jar")); - assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2602002-javadoc.jar")); + assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2603002.jar")); + assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2603002-sources.jar")); + assertTrue(ci.contains("SkysGrassSlabs-1.1.2.2603002-javadoc.jar")); assertTrue(ci.contains("if-no-files-found: error")); assertTrue(ci.contains("java-version: '25.0.3+9.0.LTS'")); assertTrue(ci.contains("--offline --no-daemon")); diff --git a/src/test/java/zone/moddev/mc/skysgrassslabs/ResourceContractTest.java b/src/test/java/zone/moddev/mc/skysgrassslabs/ResourceContractTest.java index 3313456..8e7df6d 100644 --- a/src/test/java/zone/moddev/mc/skysgrassslabs/ResourceContractTest.java +++ b/src/test/java/zone/moddev/mc/skysgrassslabs/ResourceContractTest.java @@ -101,12 +101,11 @@ public void snowyGrassSlabModelsUseUntintedSnowCaps() throws Exception { } @Test - public void grassSlabUsesComponentAwareSilkTouchPredicate() throws Exception { + public void grassSlabUsesTargetNativeSilkTouchCondition() throws Exception { String loot = Files.readString(Path.of( "src/main/resources/data/skysgrassslabs/loot_table/blocks/grass_slab.json")); - assertTrue(loot.contains("\"predicates\"")); - assertTrue(loot.contains("\"minecraft:enchantments\"")); - assertTrue(loot.contains("\"enchantments\": \"minecraft:silk_touch\"")); + assertTrue(loot.contains("\"condition\": \"minecraft:tool/can_silk_touch\"")); + assertFalse(loot.contains("\"predicates\"")); } @Test @@ -116,7 +115,7 @@ public void turfRecipeUsesStableCustomSerializer() throws Exception { assertTrue(recipe.contains("skysgrassslabs:turf_cutting")); String implementation = Files.readString(Path.of( "src/main/java/zone/moddev/mc/skysgrassslabs/recipe/TurfCuttingRecipe.java")); - assertTrue(implementation.contains("canPerformAction(ItemAbilities.SHOVEL_FLATTEN)")); + assertTrue(implementation.contains("ShovelSupport.isShovel(stack)")); } private static void assertParses(Path path) {