Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ jobs:
if-no-files-found: error
retention-days: 30
path: |
build/libs/SkysGrassSlabs-1.1.1.121012.jar
build/libs/SkysGrassSlabs-1.1.1.121012-sources.jar
build/libs/SkysGrassSlabs-1.1.1.121012-javadoc.jar
build/libs/SkysGrassSlabs-1.2.0.121012.jar
build/libs/SkysGrassSlabs-1.2.0.121012-sources.jar
build/libs/SkysGrassSlabs-1.2.0.121012-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.md

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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 1.21.1. Version `1.1.1.121012` requires NeoForge
This branch is for Minecraft 1.21.1. Version `1.2.0.121012` requires NeoForge
21.1.247 or a compatible NeoForge 21.1 build and Java 21.

## Installation
Expand Down Expand Up @@ -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.1.121012.md)
- [Release notes](docs/RELEASE-1.2.0.121012.md)

## License

Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ tasks.register('writeReleaseChecksums') {
group = 'verification'
dependsOn tasks.named('verifyReleaseArtifacts')
def output = layout.buildDirectory.file('release/SHA256SUMS')
inputs.files(releaseFiles.map { names -> names.collect { layout.buildDirectory.file("libs/${it}") } })
outputs.file(output)
doLast {
File libs = layout.buildDirectory.dir('libs').get().asFile
Expand Down Expand Up @@ -573,6 +574,8 @@ tasks.matching { it.name == 'prepareRunGameTestServer' || it.name == 'runGameTes
}

tasks.withType(AbstractArchiveTask).configureEach {
// Empty resource directories should not change the jar.
includeEmptyDirs = false
preserveFileTimestamps = false
reproducibleFileOrder = true
}
7 changes: 6 additions & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Configuration

NeoForge creates `config/skysgrassslabs-common.toml` after the mod starts for the
Forge creates `config/skysgrassslabs-common.toml` after the mod starts for the
first time. Stop the game or server before editing it, then restart to apply
changes. On a dedicated server, the server's configuration controls world
generation and world upgrades.
Expand All @@ -13,9 +13,14 @@ kept. An existing TOML file always takes priority.

`worldgen.generateGrassSlabs=true`

In the Mods list, select Sky’s Grass Slabs and open Config to edit these
choices. They take effect the next time Minecraft starts.

When enabled, suitable one block slopes in newly generated Overworld chunks
may receive grass slabs. Set this to `false` to leave new terrain unchanged.
The setting never adds or removes slabs in chunks that already exist.
If Sky’s Terrain Smoother is installed, it handles generation for both mods.
This setting still controls whether that smoother places grass pieces.

## Legacy slab replacement

Expand Down
1 change: 1 addition & 0 deletions docs/GAMEPLAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enabled is removed without dropping an item.

- Three dirt blocks in a horizontal row make six dirt slabs.
- Three grass blocks in a horizontal row make six grass slabs.
- Two matching dirt or grass slabs side by side make their original block.
- A dirt block and a recognised seed make a grass block.
- A dirt slab and a recognised seed make a grass slab.
- A grass block or grass slab together with a compatible shovel makes one
Expand Down
12 changes: 12 additions & 0 deletions docs/RELEASE-1.2.0.121012.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Sky's Grass Slabs 1.2.0 for Minecraft 1.21.1

The Mods list now has a Config button for grass smoothing and recovery
settings. Changes take effect after a restart.

Two matching dirt or grass slabs can be crafted back into their original
block. With Sky's Building Pieces installed, grass can also spread between
the smaller grass pieces. Sky's Terrain Smoother can handle the shared
generation pass when installed.

Existing block and item IDs, saved data and settings carry forward. This
build requires Minecraft 1.21.1, NeoForge 21.1.247 and Java 21.
19 changes: 9 additions & 10 deletions docs/WORLD-UPGRADES.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Upgrading an older world

Always make a complete copy of the world before changing its Minecraft or
NeoForge version. Test the copy first and keep the original until the upgraded
Forge version. Test the copy first and keep the original until the upgraded
world has been checked in game.

## Sky's Grass Slabs content

Install `SkysGrassSlabs-1.1.1.121012.jar` before opening a world that used an
Install `SkysGrassSlabs-1.2.0.121012.jar` before opening a world that used an
earlier release. The permanent dirt slab, grass slab, path slab and turf IDs
are unchanged.

Expand All @@ -15,13 +15,12 @@ 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 1.21.1 worlds already use named block states. Their
Minecraft 1.13.2 through 1.20.6 worlds already use named block states. Their
orientation, snowy and waterlogged states load through Minecraft's normal
upgrade process.

World smoothing still affects only newly generated chunks. Chunks indexed as
part of an older world are not treated as new terrain by the smoothing
feature.
World smoothing affects only new terrain. It skips chunks already saved in
your world, even if Minecraft still needs to finish decorating them.

## Supported older slab IDs

Expand All @@ -30,7 +29,7 @@ original mod is absent. Slab orientation, stack counts and custom item data are
retained.

Other older shapes such as stairs, vertical slabs, corners and steps are not
part of this migration. NeoForge may report them as missing and may remove them
part of this migration. Forge may report them as missing and may remove them
after offering its normal backup and confirmation screen. Do not continue
unless losing those unsupported blocks is acceptable for that copy of the
world.
Expand All @@ -56,7 +55,7 @@ carpet produces an item which is converted to turf.

Stairs, dirt carpet, dirt path carpet, mycelium variants and podzol variants do
not have matching blocks in Sky's Grass Slabs. They are not converted. Read
NeoForge's complete missing content warning before continuing without the older
Forge's complete missing content warning before continuing without the older
mod.

When both mods remain installed, their content is left alone by default. See
Expand All @@ -65,9 +64,9 @@ 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 1.21.1 build.
2. Update Forge and every required mod to a matching Minecraft 1.21.1 build.
3. Install the 1.21.1 Sky's Grass Slabs jar before the first upgraded start.
4. Read the complete NeoForge missing content list before confirming it.
4. Read the complete Forge 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.

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,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.1.121012
mod_version=1.2.0.121012
# 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
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import zone.moddev.mc.skysgrassslabs.SkysGrassSlabs;
import zone.moddev.mc.skysgrassslabs.api.GrassSlabsApi;
import zone.moddev.mc.skysgrassslabs.block.DirtSlabBlock;
import zone.moddev.mc.skysgrassslabs.block.GrassSlabBlock;
import zone.moddev.mc.skysgrassslabs.block.GrassSpread;
Expand Down Expand Up @@ -267,14 +268,17 @@ public static void snowPlantsBonemealAndWaterAreTopAware(GameTestHelper helper)
require(helper, grass.canSustainPlant(bottom, helper.getLevel(), pos, Direction.UP,
Blocks.DANDELION.defaultBlockState()).isFalse(),
"bottom grass slab sustained a plant");
require(helper, !grass.canSustainPlant(top, helper.getLevel(), pos, Direction.UP,
Blocks.DANDELION.defaultBlockState()).isFalse(),
"top grass slab rejected a plant");

helper.getLevel().setBlock(pos, top, Block.UPDATE_ALL);
require(helper, Blocks.DANDELION.defaultBlockState().canSurvive(helper.getLevel(), pos.above())
&& Blocks.OAK_SAPLING.defaultBlockState().canSurvive(helper.getLevel(), pos.above()),
"flowers and saplings must survive on a top grass slab");
require(helper, !Blocks.WHEAT.defaultBlockState().canSurvive(helper.getLevel(), pos.above()),
"grass slabs must not replace farmland");
require(helper, Blocks.SNOW.defaultBlockState().canSurvive(helper.getLevel(), pos.above()),
"snow did not survive on a top grass slab");
helper.getLevel().setBlock(pos, bottom, Block.UPDATE_ALL);
require(helper, !Blocks.DANDELION.defaultBlockState().canSurvive(helper.getLevel(), pos.above()),
"a flower survived on a bottom grass slab");
require(helper, !Blocks.SNOW.defaultBlockState().canSurvive(helper.getLevel(), pos.above()),
"snow survived on a bottom grass slab");
BlockState snowBlockAppearance = grass.updateShape(top, Direction.UP,
Expand Down Expand Up @@ -690,6 +694,51 @@ private static boolean matchesSeedRecipe(CraftingRecipe recipe, GameTestHelper h
.is(ModBlocks.GRASS_SLAB_ITEM.get());
}

@GameTest(template = EMPTY, batch = "slabs_family")
public static void sharedGrassFormsKeepTheirOrientationAndWater(GameTestHelper helper) {
for (SlabType half : new SlabType[] {SlabType.BOTTOM, SlabType.TOP}) {
for (boolean waterlogged : new boolean[] {false, true}) {
BlockState dirt = ModBlocks.DIRT_SLAB.get().defaultBlockState()
.setValue(SlabBlock.TYPE, half).setValue(SlabBlock.WATERLOGGED, waterlogged);
BlockState grass = GrassSlabsApi.grassFor(dirt);
require(helper, grass != null && grass.is(ModBlocks.GRASS_SLAB.get()),
"The shared catalogue is missing a native grass slab form");
require(helper, grass.getValue(SlabBlock.TYPE) == half
&& grass.getValue(SlabBlock.WATERLOGGED) == waterlogged,
"The shared catalogue changed a slab's orientation or water");
require(helper, GrassSlabsApi.dirtFor(grass) == dirt
&& GrassSlabsApi.isSpreadingGrassForm(grass) == !waterlogged,
"The shared catalogue changed the dirt form or wet grass behaviour");
}
}
helper.succeed();
}

@GameTest(template = EMPTY, batch = "slabs_family")
public static void twoMatchingSlabsCraftBackToOneBlock(GameTestHelper helper) {
verifyRecombination(helper, "dirt_from_slabs",
new ItemStack(ModBlocks.DIRT_SLAB_ITEM.get()), Blocks.DIRT);
verifyRecombination(helper, "grass_from_slabs",
new ItemStack(ModBlocks.GRASS_SLAB_ITEM.get()), Blocks.GRASS_BLOCK);
helper.succeed();
}

private static void verifyRecombination(GameTestHelper helper, String recipeName,
ItemStack slab, Block fullBlock) {
CraftingRecipe recipe = (CraftingRecipe) helper.getLevel().getRecipeManager()
.byKey(ResourceLocation.fromNamespaceAndPath(SkysGrassSlabs.MOD_ID, recipeName))
.orElseThrow().value();
CraftingInput grid = craftingGrid(2, 1, slab.copy(), slab.copy());
require(helper, recipe.matches(grid, helper.getLevel()),
"Matching slabs did not fit the recombination recipe");
ItemStack result = recipe.assemble(grid, helper.getLevel().registryAccess());
require(helper, result.is(fullBlock.asItem()) && result.getCount() == 1,
"Two slabs must give back exactly one full block");
CraftingInput singleSlab = craftingGrid(1, 1, slab.copy());
require(helper, !recipe.matches(singleSlab, helper.getLevel()),
"A single slab must not give back a full block");
}

private static CraftingInput craftingGrid(int width, int height, ItemStack... stacks) {
NonNullList<ItemStack> items = NonNullList.withSize(width * height, ItemStack.EMPTY);
for (int slot = 0; slot < stacks.length; ++slot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public static void smoothingIsBorderSafeAndIdempotent(GameTestHelper helper) {

BlockPos center = new BlockPos(owner.getMinBlockX() + 8, y, owner.getMinBlockZ() + 8);
makeOneBlockRise(helper, center, center.east());
helper.getLevel().setBlock(center.above(), Blocks.SHORT_GRASS.defaultBlockState(),
Block.UPDATE_ALL);

BlockPos border = new BlockPos(owner.getMinBlockX(), y, owner.getMinBlockZ() + 8);
makeOneBlockRise(helper, border, border.west());
Expand Down Expand Up @@ -85,7 +87,7 @@ public static void smoothingIsBorderSafeAndIdempotent(GameTestHelper helper) {
center, NoneFeatureConfiguration.INSTANCE);
require(helper, feature.place(context), "controlled feature pass made no changes");
require(helper, helper.getLevel().getBlockState(center.above())
.is(ModBlocks.GRASS_SLAB.get()), "one-block transition was not smoothed");
.is(ModBlocks.GRASS_SLAB.get()), "short grass prevented a valid one-block transition");
require(helper, helper.getLevel().getBlockState(border.above())
.is(ModBlocks.GRASS_SLAB.get()), "owning-chunk border transition was not smoothed");
require(helper, helper.getLevel().getBlockState(border.west().above())
Expand Down
35 changes: 33 additions & 2 deletions src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import net.minecraft.world.level.block.SlabBlock;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent;
import net.neoforged.fml.loading.FMLEnvironment;
import zone.moddev.mc.skysgrassslabs.api.GrassSlabsApi;
import zone.moddev.mc.skysgrassslabs.client.ClientConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import zone.moddev.mc.skysgrassslabs.compat.BuildingBricksCompat;
Expand All @@ -27,13 +35,13 @@ 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.1.121012";
public static final String VERSION = "1.2.0.121012";
public static final Logger LOGGER = LogManager.getLogger();

/** Registers content, configuration, world generation, and persistent state. */
public SkysGrassSlabs(IEventBus modBus, ModContainer modContainer) {
SkysGrassSlabsConfig.migrateLegacyConfig();
SkysGrassSlabsConfig.register(modContainer);
SkysGrassSlabsConfig.register(modBus, modContainer);
ModBlocks.register(modBus);
ModRecipes.register(modBus);
WorldgenBootstrap.register(modBus);
Expand All @@ -44,10 +52,33 @@ public SkysGrassSlabs(IEventBus modBus, ModContainer modContainer) {
GrassSlabsMigrationHandler.register();
CommonEvents.register();
registerGameTests(modBus);
modBus.addListener(this::setupGrassForms);
modBus.addListener(this::finishGrassRegistration);
if (FMLEnvironment.dist == Dist.CLIENT) {
ClientConfig.register(modContainer);
}

NeoForge.EVENT_BUS.addListener(this::onServerStarted);
}

private void setupGrassForms(FMLCommonSetupEvent event) {
SkysGrassSlabsConfig.activateSettingsForThisRun();
GrassSlabsApi.configurationLoaded();
for (SlabType half : new SlabType[] {SlabType.BOTTOM, SlabType.TOP}) {
for (boolean waterlogged : new boolean[] {false, true}) {
GrassSlabsApi.registerGrassForm(
ModBlocks.DIRT_SLAB.get().defaultBlockState()
.setValue(SlabBlock.TYPE, half).setValue(SlabBlock.WATERLOGGED, waterlogged),
ModBlocks.GRASS_SLAB.get().defaultBlockState()
.setValue(SlabBlock.TYPE, half).setValue(SlabBlock.WATERLOGGED, waterlogged));
}
}
}

private void finishGrassRegistration(FMLLoadCompleteEvent event) {
event.enqueueWork(GrassSlabsApi::freeze);
}

private static void registerGameTests(IEventBus modBus) {
try {
Class<?> bootstrap = Class.forName(
Expand Down
Loading
Loading